<?php

/*******************************************************************************
* TTFontFile class                                                             *
*                                                                              *
* Version:  1.05		                                                       *
* Date:     2011-07-21                                                         *
* Author:   Ian Back <ianb@bpm1.com>                                           *
* License:  LGPL                                                               *
* Copyright (c) Ian Back, 2010                                                 *
* This class is based on The ReportLab Open Source PDF library                 *
* written in Python - http://www.reportlab.com/software/opensource/            *
* together with ideas from the OpenOffice source code and others.              * 
* This header must be retained in any redistribution or                        *
* modification of the file.                                                    *
*                                                                              *
*******************************************************************************/

// Define the value used in the "head" table of a created TTF file
// 0x74727565 "true" for Mac
// 0x00010000 for Windows
// Either seems to work for a font embedded in a PDF file
// when read by Adobe Reader on a Windows PC(!)
if (! defined ( '_TTF_MAC_HEADER' ))
	define ( "_TTF_MAC_HEADER", false );
	
	// Recalculate correct metadata/profiles when making subset fonts (not
	// SIP/SMP)
	// e.g. xMin, xMax, maxNContours
if (! defined ( '_RECALC_PROFILE' ))
	define ( "_RECALC_PROFILE", false );
	
	// TrueType Font Glyph operators
define ( "GF_WORDS", (1 << 0) );
define ( "GF_SCALE", (1 << 3) );
define ( "GF_MORE", (1 << 5) );
define ( "GF_XYSCALE", (1 << 6) );
define ( "GF_TWOBYTWO", (1 << 7) );
class TTFontFile {
	var $panose;
	var $maxUni;
	var $sFamilyClass;
	var $sFamilySubClass;
	var $sipset;
	var $smpset;
	var $_pos;
	var $numTables;
	var $searchRange;
	var $entrySelector;
	var $rangeShift;
	var $tables;
	var $otables;
	var $filename;
	var $fh;
	var $glyphPos;
	var $charToGlyph;
	var $ascent;
	var $descent;
	var $name;
	var $familyName;
	var $styleName;
	var $fullName;
	var $uniqueFontID;
	var $unitsPerEm;
	var $bbox;
	var $capHeight;
	var $stemV;
	var $italicAngle;
	var $flags;
	var $underlinePosition;
	var $underlineThickness;
	var $charWidths;
	var $defaultWidth;
	var $maxStrLenRead;
	var $numTTCFonts;
	var $TTCFonts;
	var $maxUniChar;
	var $kerninfo;
	function TTFontFile() {
		$this->maxStrLenRead = 200000; // Maximum size of glyf table to read in as
			                               // string (otherwise reads each glyph from
			                               // file)
	}
	function getMetrics($file, $TTCfontID = 0, $debug = false, $BMPonly = false, $kerninfo = false) {
		$this->filename = $file;
		$this->fh = fopen ( $file, 'rb' ) or die ( 'Can\'t open file ' . $file );
		$this->_pos = 0;
		$this->charWidths = '';
		$this->glyphPos = array ();
		$this->charToGlyph = array ();
		$this->tables = array ();
		$this->otables = array ();
		$this->kerninfo = array ();
		$this->ascent = 0;
		$this->descent = 0;
		$this->numTTCFonts = 0;
		$this->TTCFonts = array ();
		$this->version = $version = $this->read_ulong ();
		$this->panose = array ();
		if ($version == 0x4F54544F)
			die ( "Postscript outlines are not supported" );
		if ($version == 0x74746366 && ! $TTCfontID)
			die ( "ERROR - You must define the TTCfontID for a TrueType Collection in config_fonts.php (" . $file . ")" );
		if (! in_array ( $version, array (
				0x00010000,
				0x74727565 
		) ) && ! $TTCfontID)
			die ( "Not a TrueType font: version=" . $version );
		if ($TTCfontID > 0) {
			$this->version = $version = $this->read_ulong (); // TTC Header version
			                                                  // now
			if (! in_array ( $version, array (
					0x00010000,
					0x00020000 
			) ))
				die ( "ERROR - Error parsing TrueType Collection: version=" . $version . " - " . $file );
			$this->numTTCFonts = $this->read_ulong ();
			for($i = 1; $i <= $this->numTTCFonts; $i ++) {
				$this->TTCFonts [$i] ['offset'] = $this->read_ulong ();
			}
			$this->seek ( $this->TTCFonts [$TTCfontID] ['offset'] );
			$this->version = $version = $this->read_ulong (); // TTFont version
				                                                  // again now
		}
		$this->readTableDirectory ( $debug );
		$this->extractInfo ( $debug, $BMPonly, $kerninfo );
		fclose ( $this->fh );
	}
	function readTableDirectory($debug = false) {
		$this->numTables = $this->read_ushort ();
		$this->searchRange = $this->read_ushort ();
		$this->entrySelector = $this->read_ushort ();
		$this->rangeShift = $this->read_ushort ();
		$this->tables = array ();
		for($i = 0; $i < $this->numTables; $i ++) {
			$record = array ();
			$record ['tag'] = $this->read_tag ();
			$record ['checksum'] = array (
					$this->read_ushort (),
					$this->read_ushort () 
			);
			$record ['offset'] = $this->read_ulong ();
			$record ['length'] = $this->read_ulong ();
			$this->tables [$record ['tag']] = $record;
		}
		if ($debug)
			$this->checksumTables ();
	}
	function checksumTables() {
		// Check the checksums for all tables
		foreach ( $this->tables as $t ) {
			if ($t ['length'] > 0 && $t ['length'] < $this->maxStrLenRead) { // 1.02
				$table = $this->get_chunk ( $t ['offset'], $t ['length'] );
				$checksum = $this->calcChecksum ( $table );
				if ($t ['tag'] == 'head') {
					$up = unpack ( 'n*', substr ( $table, 8, 4 ) );
					$adjustment [0] = $up [1];
					$adjustment [1] = $up [2];
					$checksum = $this->sub32 ( $checksum, $adjustment );
				}
				$xchecksum = $t ['checksum'];
				if ($xchecksum != $checksum)
					die ( sprintf ( 'TTF file "%s": invalid checksum %s table: %s (expected %s)', $this->filename, dechex ( $checksum [0] ) . dechex ( $checksum [1] ), $t ['tag'], dechex ( $xchecksum [0] ) . dechex ( $xchecksum [1] ) ) );
			}
		}
	}
	function sub32($x, $y) {
		$xlo = $x [1];
		$xhi = $x [0];
		$ylo = $y [1];
		$yhi = $y [0];
		if ($ylo > $xlo) {
			$xlo += 1 << 16;
			$yhi += 1;
		}
		$reslo = $xlo - $ylo;
		if ($yhi > $xhi) {
			$xhi += 1 << 16;
		}
		$reshi = $xhi - $yhi;
		$reshi = $reshi & 0xFFFF;
		return array (
				$reshi,
				$reslo 
		);
	}
	function calcChecksum($data) {
		if (strlen ( $data ) % 4) {
			$data .= str_repeat ( "\0", (4 - (strlen ( $data ) % 4)) );
		}
		$len = strlen ( $data );
		$hi = 0x0000;
		$lo = 0x0000;
		for($i = 0; $i < $len; $i += 4) {
			$hi += (ord ( $data [$i] ) << 8) + ord ( $data [$i + 1] );
			$lo += (ord ( $data [$i + 2] ) << 8) + ord ( $data [$i + 3] );
			$hi += ($lo >> 16) & 0xFFFF; // mPDF 5.3.07
			$lo = $lo & 0xFFFF;
		}
		return array (
				$hi,
				$lo 
		);
	}
	function get_table_pos($tag) {
		$offset = $this->tables [$tag] ['offset'];
		$length = $this->tables [$tag] ['length'];
		return array (
				$offset,
				$length 
		);
	}
	function seek($pos) {
		$this->_pos = $pos;
		fseek ( $this->fh, $this->_pos );
	}
	function skip($delta) {
		$this->_pos = $this->_pos + $delta;
		// fseek($this->fh,$this->_pos);
		fseek ( $this->fh, $delta, SEEK_CUR ); // mPDF 5.3.19
	}
	function seek_table($tag, $offset_in_table = 0) {
		$tpos = $this->get_table_pos ( $tag );
		$this->_pos = $tpos [0] + $offset_in_table;
		fseek ( $this->fh, $this->_pos );
		return $this->_pos;
	}
	function read_tag() {
		$this->_pos += 4;
		return fread ( $this->fh, 4 );
	}
	function read_short() {
		$this->_pos += 2;
		$s = fread ( $this->fh, 2 );
		$a = (ord ( $s [0] ) << 8) + ord ( $s [1] );
		if ($a & (1 << 15)) {
			$a = ($a - (1 << 16));
		}
		return $a;
	}
	function unpack_short($s) {
		$a = (ord ( $s [0] ) << 8) + ord ( $s [1] );
		if ($a & (1 << 15)) {
			$a = ($a - (1 << 16));
		}
		return $a;
	}
	function read_ushort() {
		$this->_pos += 2;
		$s = fread ( $this->fh, 2 );
		return (ord ( $s [0] ) << 8) + ord ( $s [1] );
	}
	function read_ulong() {
		$this->_pos += 4;
		$s = fread ( $this->fh, 4 );
		// if large uInt32 as an integer, PHP converts it to -ve
		return (ord ( $s [0] ) * 16777216) + (ord ( $s [1] ) << 16) + (ord ( $s [2] ) << 8) + ord ( $s [3] ); // 16777216
			                                                                                                      // =
			                                                                                                      // 1<<24
	}
	function get_ushort($pos) {
		fseek ( $this->fh, $pos );
		$s = fread ( $this->fh, 2 );
		return (ord ( $s [0] ) << 8) + ord ( $s [1] );
	}
	function get_ulong($pos) {
		fseek ( $this->fh, $pos );
		$s = fread ( $this->fh, 4 );
		// iF large uInt32 as an integer, PHP converts it to -ve
		return (ord ( $s [0] ) * 16777216) + (ord ( $s [1] ) << 16) + (ord ( $s [2] ) << 8) + ord ( $s [3] ); // 16777216
			                                                                                                      // =
			                                                                                                      // 1<<24
	}
	function pack_short($val) {
		if ($val < 0) {
			$val = abs ( $val );
			$val = ~ $val;
			$val += 1;
		}
		return pack ( "n", $val );
	}
	function splice($stream, $offset, $value) {
		return substr ( $stream, 0, $offset ) . $value . substr ( $stream, $offset + strlen ( $value ) );
	}
	function _set_ushort($stream, $offset, $value) {
		$up = pack ( "n", $value );
		return $this->splice ( $stream, $offset, $up );
	}
	
	// mPDF 5.0
	function _set_short($stream, $offset, $val) {
		if ($val < 0) {
			$val = abs ( $val );
			$val = ~ $val;
			$val += 1;
		}
		$up = pack ( "n", $val );
		return $this->splice ( $stream, $offset, $up );
	}
	function get_chunk($pos, $length) {
		fseek ( $this->fh, $pos );
		if ($length < 1) {
			return '';
		}
		return (fread ( $this->fh, $length ));
	}
	function get_table($tag) {
		list ( $pos, $length ) = $this->get_table_pos ( $tag );
		if ($length == 0) {
			return '';
		}
		fseek ( $this->fh, $pos );
		return (fread ( $this->fh, $length ));
	}
	function add($tag, $data) {
		if ($tag == 'head') {
			$data = $this->splice ( $data, 8, "\0\0\0\0" );
		}
		$this->otables [$tag] = $data;
	}
	
	// ///////////////////////////////////////////////////////////////////////////////////////
	function getCTG($file, $TTCfontID = 0, $debug = false) {
		$this->filename = $file;
		$this->fh = fopen ( $file, 'rb' ) or die ( 'Can\'t open file ' . $file );
		$this->_pos = 0;
		$this->charWidths = '';
		$this->glyphPos = array ();
		$this->charToGlyph = array ();
		$this->tables = array ();
		$this->numTTCFonts = 0;
		$this->TTCFonts = array ();
		$this->skip ( 4 );
		if ($TTCfontID > 0) {
			$this->version = $version = $this->read_ulong (); // TTC Header version
			                                                  // now
			if (! in_array ( $version, array (
					0x00010000,
					0x00020000 
			) ))
				die ( "ERROR - Error parsing TrueType Collection: version=" . $version . " - " . $file );
			$this->numTTCFonts = $this->read_ulong ();
			for($i = 1; $i <= $this->numTTCFonts; $i ++) {
				$this->TTCFonts [$i] ['offset'] = $this->read_ulong ();
			}
			$this->seek ( $this->TTCFonts [$TTCfontID] ['offset'] );
			$this->version = $version = $this->read_ulong (); // TTFont version
				                                                  // again now
		}
		$this->readTableDirectory ( $debug );
		
		// cmap - Character to glyph index mapping table
		$cmap_offset = $this->seek_table ( "cmap" );
		$this->skip ( 2 );
		$cmapTableCount = $this->read_ushort ();
		$unicode_cmap_offset = 0;
		for($i = 0; $i < $cmapTableCount; $i ++) {
			$platformID = $this->read_ushort ();
			$encodingID = $this->read_ushort ();
			$offset = $this->read_ulong ();
			$save_pos = $this->_pos;
			if ($platformID == 3 && $encodingID == 1) { // Microsoft, Unicode
				$format = $this->get_ushort ( $cmap_offset + $offset );
				if ($format == 4) {
					$unicode_cmap_offset = $cmap_offset + $offset;
					break;
				}
			} else if ($platformID == 0) { // Unicode -- assume all encodings are
			                               // compatible
				$format = $this->get_ushort ( $cmap_offset + $offset );
				if ($format == 4) {
					$unicode_cmap_offset = $cmap_offset + $offset;
					break;
				}
			}
			$this->seek ( $save_pos );
		}
		
		$glyphToChar = array ();
		$charToGlyph = array ();
		$this->getCMAP4 ( $unicode_cmap_offset, $glyphToChar, $charToGlyph );
		
		fclose ( $this->fh );
		return ($charToGlyph);
	}
	
	// ///////////////////////////////////////////////////////////////////////////////////////
	function getTTCFonts($file) {
		$this->filename = $file;
		$this->fh = fopen ( $file, 'rb' );
		if (! $this->fh) {
			return ('ERROR - Can\'t open file ' . $file);
		}
		$this->numTTCFonts = 0;
		$this->TTCFonts = array ();
		$this->version = $version = $this->read_ulong ();
		if ($version == 0x74746366) {
			$this->version = $version = $this->read_ulong (); // TTC Header version
			                                                  // now
			if (! in_array ( $version, array (
					0x00010000,
					0x00020000 
			) ))
				return ("ERROR - Error parsing TrueType Collection: version=" . $version . " - " . $file);
		} else {
			return ("ERROR - Not a TrueType Collection: version=" . $version . " - " . $file);
		}
		$this->numTTCFonts = $this->read_ulong ();
		for($i = 1; $i <= $this->numTTCFonts; $i ++) {
			$this->TTCFonts [$i] ['offset'] = $this->read_ulong ();
		}
	}
	
	// Used to get font information from files in directory for mPDF config
	function extractCoreInfo($file, $TTCfontID = 0) {
		$this->filename = $file;
		$this->fh = fopen ( $file, 'rb' );
		if (! $this->fh) {
			return ('ERROR - Can\'t open file ' . $file);
		}
		$this->_pos = 0;
		$this->charWidths = '';
		$this->glyphPos = array ();
		$this->charToGlyph = array ();
		$this->tables = array ();
		$this->otables = array ();
		$this->ascent = 0;
		$this->descent = 0;
		$this->numTTCFonts = 0;
		$this->TTCFonts = array ();
		$this->version = $version = $this->read_ulong ();
		$this->panose = array (); // mPDF 5.0
		if ($version == 0x4F54544F)
			return ("ERROR - NOT ADDED as Postscript outlines are not supported - " . $file);
		if ($version == 0x74746366) {
			if ($TTCfontID > 0) {
				$this->version = $version = $this->read_ulong (); // TTC Header
				                                                  // version now
				if (! in_array ( $version, array (
						0x00010000,
						0x00020000 
				) ))
					return ("ERROR - NOT ADDED as Error parsing TrueType Collection: version=" . $version . " - " . $file);
			} else
				return ("ERROR - Error parsing TrueType Collection - " . $file);
			$this->numTTCFonts = $this->read_ulong ();
			for($i = 1; $i <= $this->numTTCFonts; $i ++) {
				$this->TTCFonts [$i] ['offset'] = $this->read_ulong ();
			}
			$this->seek ( $this->TTCFonts [$TTCfontID] ['offset'] );
			$this->version = $version = $this->read_ulong (); // TTFont version
			                                                  // again now
			$this->readTableDirectory ( false );
		} else {
			if (! in_array ( $version, array (
					0x00010000,
					0x74727565 
			) ))
				return ("ERROR - NOT ADDED as Not a TrueType font: version=" . $version . " - " . $file);
			$this->readTableDirectory ( false );
		}
		
		/*
		 * Included for testing... $cmap_offset = $this->seek_table("cmap"); $this->skip(2); $cmapTableCount = $this->read_ushort(); $unicode_cmap_offset = 0; for ($i=0;$i<$cmapTableCount;$i++) { $x[$i]['platformId'] = $this->read_ushort(); $x[$i]['encodingId'] = $this->read_ushort(); $x[$i]['offset'] = $this->read_ulong(); $save_pos = $this->_pos; $x[$i]['format'] = $this->get_ushort($cmap_offset + $x[$i]['offset'] ); $this->seek($save_pos ); } print_r($x); exit;
		 */
		// /////////////////////////////////
		// name - Naming table
		// /////////////////////////////////
		$name_offset = $this->seek_table ( "name" );
		$format = $this->read_ushort ();
		if ($format != 0 && $format != 1) // mPDF 5.3.73
			return ("ERROR - NOT ADDED as Unknown name table format " . $format . " - " . $file);
		$numRecords = $this->read_ushort ();
		$string_data_offset = $name_offset + $this->read_ushort ();
		$names = array (
				1 => '',
				2 => '',
				3 => '',
				4 => '',
				6 => '' 
		);
		$K = array_keys ( $names );
		$nameCount = count ( $names );
		for($i = 0; $i < $numRecords; $i ++) {
			$platformId = $this->read_ushort ();
			$encodingId = $this->read_ushort ();
			$languageId = $this->read_ushort ();
			$nameId = $this->read_ushort ();
			$length = $this->read_ushort ();
			$offset = $this->read_ushort ();
			if (! in_array ( $nameId, $K ))
				continue;
			$N = '';
			if ($platformId == 3 && $encodingId == 1 && $languageId == 0x409) { // Microsoft,
			                                                                    // Unicode,
			                                                                    // US
			                                                                    // English,
			                                                                    // PS
			                                                                    // Name
				$opos = $this->_pos;
				$this->seek ( $string_data_offset + $offset );
				if ($length % 2 != 0)
					$length += 1;
				$length /= 2;
				$N = '';
				while ( $length > 0 ) {
					$char = $this->read_ushort ();
					$N .= (chr ( $char ));
					$length -= 1;
				}
				$this->_pos = $opos;
				$this->seek ( $opos );
			} else if ($platformId == 1 && $encodingId == 0 && $languageId == 0) { // Macintosh,
			                                                                       // Roman,
			                                                                       // English,
			                                                                       // PS
			                                                                       // Name
				$opos = $this->_pos;
				$N = $this->get_chunk ( $string_data_offset + $offset, $length );
				$this->_pos = $opos;
				$this->seek ( $opos );
			}
			if ($N && $names [$nameId] == '') {
				$names [$nameId] = $N;
				$nameCount -= 1;
				if ($nameCount == 0)
					break;
			}
		}
		if ($names [6])
			$psName = preg_replace ( '/ /', '-', $names [6] );
		else if ($names [4])
			$psName = preg_replace ( '/ /', '-', $names [4] );
		else if ($names [1])
			$psName = preg_replace ( '/ /', '-', $names [1] );
		else
			$psName = '';
		if (! $names [1] && ! $psName)
			return ("ERROR - NOT ADDED as Could not find valid font name - " . $file);
		$this->name = $psName;
		if ($names [1]) {
			$this->familyName = $names [1];
		} else {
			$this->familyName = $psName;
		}
		if ($names [2]) {
			$this->styleName = $names [2];
		} else {
			$this->styleName = 'Regular';
		}
		
		// /////////////////////////////////
		// head - Font header table
		// /////////////////////////////////
		$this->seek_table ( "head" );
		$ver_maj = $this->read_ushort ();
		$ver_min = $this->read_ushort ();
		if ($ver_maj != 1)
			return ('ERROR - NOT ADDED as Unknown head table version ' . $ver_maj . '.' . $ver_min . " - " . $file);
		$this->fontRevision = $this->read_ushort () . $this->read_ushort ();
		$this->skip ( 4 );
		$magic = $this->read_ulong ();
		if ($magic != 0x5F0F3CF5)
			return ('ERROR - NOT ADDED as Invalid head table magic ' . $magic . " - " . $file);
		$this->skip ( 2 );
		$this->unitsPerEm = $unitsPerEm = $this->read_ushort ();
		$scale = 1000 / $unitsPerEm;
		$this->skip ( 24 );
		$macStyle = $this->read_short ();
		$this->skip ( 4 );
		$indexLocFormat = $this->read_short ();
		
		// /////////////////////////////////
		// OS/2 - OS/2 and Windows metrics table
		// /////////////////////////////////
		$sFamily = '';
		$panose = '';
		$fsSelection = '';
		if (isset ( $this->tables ["OS/2"] )) {
			$this->seek_table ( "OS/2" );
			$this->skip ( 30 );
			$sF = $this->read_short ();
			$sFamily = ($sF >> 8);
			$this->_pos += 10; // PANOSE = 10 byte length
			$panose = fread ( $this->fh, 10 );
			$this->panose = array ();
			for($p = 0; $p < strlen ( $panose ); $p ++) {
				$this->panose [] = ord ( $panose [$p] );
			}
			$this->skip ( 20 );
			$fsSelection = $this->read_short ();
		}
		
		// /////////////////////////////////
		// post - PostScript table
		// /////////////////////////////////
		$this->seek_table ( "post" );
		$this->skip ( 4 );
		$this->italicAngle = $this->read_short () + $this->read_ushort () / 65536.0;
		$this->skip ( 4 );
		$isFixedPitch = $this->read_ulong ();
		
		// /////////////////////////////////
		// cmap - Character to glyph index mapping table
		// /////////////////////////////////
		$cmap_offset = $this->seek_table ( "cmap" );
		$this->skip ( 2 );
		$cmapTableCount = $this->read_ushort ();
		$unicode_cmap_offset = 0;
		for($i = 0; $i < $cmapTableCount; $i ++) {
			$platformID = $this->read_ushort ();
			$encodingID = $this->read_ushort ();
			$offset = $this->read_ulong ();
			$save_pos = $this->_pos;
			if (($platformID == 3 && $encodingID == 1) || $platformID == 0) { // Microsoft,
			                                                                  // Unicode
				$format = $this->get_ushort ( $cmap_offset + $offset );
				if ($format == 4) {
					if (! $unicode_cmap_offset)
						$unicode_cmap_offset = $cmap_offset + $offset;
				}
			} else if ((($platformID == 3 && $encodingID == 10) || $platformID == 0)) { // Microsoft,
			                                                                            // Unicode
			                                                                            // Format
			                                                                            // 12
			                                                                            // table
			                                                                            // HKCS
				$format = $this->get_ushort ( $cmap_offset + $offset );
				if ($format == 12) {
					$unicode_cmap_offset = $cmap_offset + $offset;
					break;
				}
			}
			$this->seek ( $save_pos );
		}
		
		if (! $unicode_cmap_offset)
			return ('ERROR - Font (' . $this->filename . ') NOT ADDED as it is not Unicode encoded, and cannot be used by mPDF');
		
		$rtl = false;
		$indic = false;
		$cjk = false;
		$sip = false;
		$smp = false;
		
		// Format 12 CMAP does characters above Unicode BMP i.e. some HKCS
		// characters U+20000 and above
		if ($format == 12) {
			$this->seek ( $unicode_cmap_offset + 4 );
			$length = $this->read_ulong ();
			$limit = $unicode_cmap_offset + $length;
			$this->skip ( 4 );
			$nGroups = $this->read_ulong ();
			for($i = 0; $i < $nGroups; $i ++) {
				$startCharCode = $this->read_ulong ();
				$endCharCode = $this->read_ulong ();
				$startGlyphCode = $this->read_ulong ();
				if (($endCharCode > 0x20000 && $endCharCode < 0x2A6DF) || ($endCharCode > 0x2F800 && $endCharCode < 0x2FA1F)) {
					$sip = true;
				} else if ($endCharCode > 0x10000 && $endCharCode < 0x1FFFF) {
					$smp = true;
				} else if (($endCharCode > 0x0590 && $endCharCode < 0x077F) || ($endCharCode > 0xFE70 && $endCharCode < 0xFEFF) || ($endCharCode > 0xFB50 && $endCharCode < 0xFDFF)) {
					$rtl = true;
				} else if ($endCharCode > 0x0900 && $endCharCode < 0x0DFF) {
					$indic = true;
				} else if (($endCharCode > 0x2E80 && $endCharCode < 0x4DC0) || ($endCharCode > 0x4E00 && $endCharCode < 0xA4CF) || ($endCharCode > 0xAC00 && $endCharCode < 0xD7AF) || ($endCharCode > 0xF900 && $endCharCode < 0xFAFF) || ($endCharCode > 0xFE30 && $endCharCode < 0xFE4F)) {
					$cjk = true;
				}
			}
		} 

		else { // Format 4 CMap
			$this->seek ( $unicode_cmap_offset + 2 );
			$length = $this->read_ushort ();
			$limit = $unicode_cmap_offset + $length;
			$this->skip ( 2 );
			
			$segCount = $this->read_ushort () / 2;
			$this->skip ( 6 );
			$endCount = array ();
			for($i = 0; $i < $segCount; $i ++) {
				$endCount [] = $this->read_ushort ();
			}
			$this->skip ( 2 );
			$startCount = array ();
			for($i = 0; $i < $segCount; $i ++) {
				$startCount [] = $this->read_ushort ();
			}
			$idDelta = array ();
			for($i = 0; $i < $segCount; $i ++) {
				$idDelta [] = $this->read_short ();
			} // ????
			  // was
			  // unsigned
			  // short
			$idRangeOffset_start = $this->_pos;
			$idRangeOffset = array ();
			for($i = 0; $i < $segCount; $i ++) {
				$idRangeOffset [] = $this->read_ushort ();
			}
			
			for($n = 0; $n < $segCount; $n ++) {
				if (($endCount [$n] > 0x0590 && $endCount [$n] < 0x077F) || ($endCount [$n] > 0xFE70 && $endCount [$n] < 0xFEFF) || ($endCount [$n] > 0xFB50 && $endCount [$n] < 0xFDFF)) {
					$rtl = true;
				} else if ($endCount [$n] > 0x0900 && $endCount [$n] < 0x0DFF) {
					$indic = true;
				} else if (($endCount [$n] > 0x2E80 && $endCount [$n] < 0x4DC0) || ($endCount [$n] > 0x4E00 && $endCount [$n] < 0xA4CF) || ($endCount [$n] > 0xAC00 && $endCount [$n] < 0xD7AF) || ($endCount [$n] > 0xF900 && $endCount [$n] < 0xFAFF) || ($endCount [$n] > 0xFE30 && $endCount [$n] < 0xFE4F)) {
					$cjk = true;
				}
			}
		}
		
		$bold = false;
		$italic = false;
		$ftype = '';
		if ($macStyle & (1 << 0)) {
			$bold = true;
		} 		// bit 0 bold
		else if ($fsSelection & (1 << 5)) {
			$bold = true;
		} // 5 BOLD Characters
		  // are emboldened
		
		if ($macStyle & (1 << 1)) {
			$italic = true;
		} 		// bit 1 italic
		else if ($fsSelection & (1 << 0)) {
			$italic = true;
		} 		// 0 ITALIC Font
		  // contains Italic
		  // characters, otherwise
		  // they are upright
		else if ($this->italicAngle != 0) {
			$italic = true;
		}
		
		if ($isFixedPitch) {
			$ftype = 'mono';
		} else if ($sFamily > 0 && $sFamily < 8) {
			$ftype = 'serif';
		} else if ($sFamily == 8) {
			$ftype = 'sans';
		} else if ($sFamily == 10) {
			$ftype = 'cursive';
		}
		// Use PANOSE
		if ($panose) {
			$bFamilyType = ord ( $panose [0] );
			if ($bFamilyType == 2) {
				$bSerifStyle = ord ( $panose [1] );
				if (! $ftype) {
					if ($bSerifStyle > 1 && $bSerifStyle < 11) {
						$ftype = 'serif';
					} else if ($bSerifStyle > 10) {
						$ftype = 'sans';
					}
				}
				$bProportion = ord ( $panose [3] );
				if ($bProportion == 9 || $bProportion == 1) {
					$ftype = 'mono';
				} // ==1
					  // i.e. No Fit
					  // needed for
					  // OCR-a and -b
			} else if ($bFamilyType == 3) {
				$ftype = 'cursive';
			}
		}
		
		fclose ( $this->fh );
		return array (
				$this->familyName,
				$bold,
				$italic,
				$ftype,
				$TTCfontID,
				$rtl,
				$indic,
				$cjk,
				$sip,
				$smp 
		);
	}
	
	// ///////////////////////////////////////////////////////////////////////////////////////
	
	// ///////////////////////////////////////////////////////////////////////////////////////
	function extractInfo($debug = false, $BMPonly = false, $kerninfo = false) {
		// /////////////////////////////////
		// name - Naming table
		// /////////////////////////////////
		$this->panose = array ();
		$this->sFamilyClass = 0;
		$this->sFamilySubClass = 0;
		
		/*
		 * Test purposes - displays table of names $name_offset = $this->seek_table("name"); $format = $this->read_ushort(); if ($format != 0 && $format != 1)	// mPDF 5.3.73 die("Unknown name table format ".$format); $numRecords = $this->read_ushort(); $string_data_offset = $name_offset + $this->read_ushort(); for ($i=0;$i<$numRecords; $i++) { $x[$i]['platformId'] = $this->read_ushort(); $x[$i]['encodingId'] = $this->read_ushort(); $x[$i]['languageId'] = $this->read_ushort(); $x[$i]['nameId'] = $this->read_ushort(); $x[$i]['length'] = $this->read_ushort(); $x[$i]['offset'] = $this->read_ushort(); $N = ''; if ($x[$i]['platformId'] == 1 && $x[$i]['encodingId'] == 0 && $x[$i]['languageId'] == 0) { // Roman $opos = $this->_pos; $N = $this->get_chunk($string_data_offset + $x[$i]['offset'] , $x[$i]['length'] ); $this->_pos = $opos; $this->seek($opos); } else { // Unicode $opos = $this->_pos; $this->seek($string_data_offset + $x[$i]['offset'] ); $length = $x[$i]['length'] ; if ($length % 2 != 0) $length -= 1; //		die("PostScript name is UTF-16BE string of odd length"); $length /= 2; $N = ''; while ($length > 0) { $char = $this->read_ushort(); $N .= (chr($char)); $length -= 1; } $this->_pos = $opos; $this->seek($opos); } $x[$i]['names'][$nameId] = $N; } print_r($x); exit;
		 */
		
		$name_offset = $this->seek_table ( "name" );
		$format = $this->read_ushort ();
		if ($format != 0 && $format != 1) // mPDF 5.3.73
			die ( "Unknown name table format " . $format );
		$numRecords = $this->read_ushort ();
		$string_data_offset = $name_offset + $this->read_ushort ();
		$names = array (
				1 => '',
				2 => '',
				3 => '',
				4 => '',
				6 => '' 
		);
		$K = array_keys ( $names );
		$nameCount = count ( $names );
		for($i = 0; $i < $numRecords; $i ++) {
			$platformId = $this->read_ushort ();
			$encodingId = $this->read_ushort ();
			$languageId = $this->read_ushort ();
			$nameId = $this->read_ushort ();
			$length = $this->read_ushort ();
			$offset = $this->read_ushort ();
			if (! in_array ( $nameId, $K ))
				continue;
			$N = '';
			if ($platformId == 3 && $encodingId == 1 && $languageId == 0x409) { // Microsoft,
			                                                                    // Unicode,
			                                                                    // US
			                                                                    // English,
			                                                                    // PS
			                                                                    // Name
				$opos = $this->_pos;
				$this->seek ( $string_data_offset + $offset );
				if ($length % 2 != 0)
					die ( "PostScript name is UTF-16BE string of odd length" );
				$length /= 2;
				$N = '';
				while ( $length > 0 ) {
					$char = $this->read_ushort ();
					$N .= (chr ( $char ));
					$length -= 1;
				}
				$this->_pos = $opos;
				$this->seek ( $opos );
			} else if ($platformId == 1 && $encodingId == 0 && $languageId == 0) { // Macintosh,
			                                                                       // Roman,
			                                                                       // English,
			                                                                       // PS
			                                                                       // Name
				$opos = $this->_pos;
				$N = $this->get_chunk ( $string_data_offset + $offset, $length );
				$this->_pos = $opos;
				$this->seek ( $opos );
			}
			if ($N && $names [$nameId] == '') {
				$names [$nameId] = $N;
				$nameCount -= 1;
				if ($nameCount == 0)
					break;
			}
		}
		if ($names [6])
			// $psName = preg_replace('/ /','-',$names[6]); // mPDF 5.2.03
			$psName = $names [6];
		else if ($names [4])
			$psName = preg_replace ( '/ /', '-', $names [4] );
		else if ($names [1])
			$psName = preg_replace ( '/ /', '-', $names [1] );
		else
			$psName = '';
		if (! $psName)
			die ( "Could not find PostScript font name: " . $this->filename );
		if ($debug) {
			for($i = 0; $i < count ( $psName ); $i ++) { // mPDF 5.3.07
				$c = $psName [$i];
				$oc = ord ( $c );
				if ($oc > 126 || strpos ( ' [](){}<>/%', $c ) !== false)
					die ( "psName=" . $psName . " contains invalid character " . $c . " ie U+" . ord ( c ) );
			}
		}
		$this->name = $psName;
		if ($names [1]) {
			$this->familyName = $names [1];
		} else {
			$this->familyName = $psName;
		}
		if ($names [2]) {
			$this->styleName = $names [2];
		} else {
			$this->styleName = 'Regular';
		}
		if ($names [4]) {
			$this->fullName = $names [4];
		} else {
			$this->fullName = $psName;
		}
		if ($names [3]) {
			$this->uniqueFontID = $names [3];
		} else {
			$this->uniqueFontID = $psName;
		}
		
		if ($names [6]) {
			$this->fullName = $names [6];
		} // mPDF 5.2.03
		  
		// /////////////////////////////////
		  // head - Font header
		  // table
		  // /////////////////////////////////
		$this->seek_table ( "head" );
		if ($debug) {
			$ver_maj = $this->read_ushort ();
			$ver_min = $this->read_ushort ();
			if ($ver_maj != 1)
				die ( 'Unknown head table version ' . $ver_maj . '.' . $ver_min );
			$this->fontRevision = $this->read_ushort () . $this->read_ushort ();
			
			$this->skip ( 4 );
			$magic = $this->read_ulong ();
			if ($magic != 0x5F0F3CF5)
				die ( 'Invalid head table magic ' . $magic );
			$this->skip ( 2 );
		} else {
			$this->skip ( 18 );
		}
		$this->unitsPerEm = $unitsPerEm = $this->read_ushort ();
		$scale = 1000 / $unitsPerEm;
		$this->skip ( 16 );
		$xMin = $this->read_short ();
		$yMin = $this->read_short ();
		$xMax = $this->read_short ();
		$yMax = $this->read_short ();
		$this->bbox = array (
				($xMin * $scale),
				($yMin * $scale),
				($xMax * $scale),
				($yMax * $scale) 
		);
		$this->skip ( 3 * 2 );
		$indexToLocFormat = $this->read_ushort ();
		$glyphDataFormat = $this->read_ushort ();
		if ($glyphDataFormat != 0)
			die ( 'Unknown glyph data format ' . $glyphDataFormat );
			
			// /////////////////////////////////
			// hhea metrics table
			// /////////////////////////////////
			// ttf2t1 seems to use this value rather than the one in OS/2 - so
			// put in for compatibility
		if (isset ( $this->tables ["hhea"] )) {
			$this->seek_table ( "hhea" );
			$this->skip ( 4 );
			$hheaAscender = $this->read_short ();
			$hheaDescender = $this->read_short ();
			$this->ascent = ($hheaAscender * $scale);
			$this->descent = ($hheaDescender * $scale);
		}
		
		// /////////////////////////////////
		// OS/2 - OS/2 and Windows metrics table
		// /////////////////////////////////
		if (isset ( $this->tables ["OS/2"] )) {
			$this->seek_table ( "OS/2" );
			$version = $this->read_ushort ();
			$this->skip ( 2 );
			$usWeightClass = $this->read_ushort ();
			$this->skip ( 2 );
			$fsType = $this->read_ushort ();
			if ($fsType == 0x0002 || ($fsType & 0x0300) != 0) {
				global $overrideTTFFontRestriction;
				if (! $overrideTTFFontRestriction)
					die ( 'ERROR - Font file ' . $this->filename . ' cannot be embedded due to copyright restrictions.' );
				$this->restrictedUse = true;
			}
			$this->skip ( 20 );
			$sF = $this->read_short ();
			$this->sFamilyClass = ($sF >> 8);
			$this->sFamilySubClass = ($sF & 0xFF);
			$this->_pos += 10; // PANOSE = 10 byte length
			$panose = fread ( $this->fh, 10 );
			$this->panose = array ();
			for($p = 0; $p < strlen ( $panose ); $p ++) {
				$this->panose [] = ord ( $panose [$p] );
			}
			$this->skip ( 26 );
			$sTypoAscender = $this->read_short ();
			$sTypoDescender = $this->read_short ();
			if (! $this->ascent)
				$this->ascent = ($sTypoAscender * $scale);
			if (! $this->descent)
				$this->descent = ($sTypoDescender * $scale);
			if ($version > 1) {
				$this->skip ( 16 );
				$sCapHeight = $this->read_short ();
				$this->capHeight = ($sCapHeight * $scale);
			} else {
				$this->capHeight = $this->ascent;
			}
		} else {
			$usWeightClass = 500;
			if (! $this->ascent)
				$this->ascent = ($yMax * $scale);
			if (! $this->descent)
				$this->descent = ($yMin * $scale);
			$this->capHeight = $this->ascent;
		}
		$this->stemV = 50 + intval ( pow ( ($usWeightClass / 65.0), 2 ) );
		
		// /////////////////////////////////
		// post - PostScript table
		// /////////////////////////////////
		$this->seek_table ( "post" );
		if ($debug) {
			$ver_maj = $this->read_ushort ();
			$ver_min = $this->read_ushort ();
			if ($ver_maj < 1 || $ver_maj > 4)
				die ( 'Unknown post table version ' . $ver_maj );
		} else {
			$this->skip ( 4 );
		}
		$this->italicAngle = $this->read_short () + $this->read_ushort () / 65536.0;
		$this->underlinePosition = $this->read_short () * $scale;
		$this->underlineThickness = $this->read_short () * $scale;
		$isFixedPitch = $this->read_ulong ();
		
		$this->flags = 4;
		
		if ($this->italicAngle != 0)
			$this->flags = $this->flags | 64;
		if ($usWeightClass >= 600)
			$this->flags = $this->flags | 262144;
		if ($isFixedPitch)
			$this->flags = $this->flags | 1;
			
			// /////////////////////////////////
			// hhea - Horizontal header table
			// /////////////////////////////////
		$this->seek_table ( "hhea" );
		if ($debug) {
			$ver_maj = $this->read_ushort ();
			$ver_min = $this->read_ushort ();
			if ($ver_maj != 1)
				die ( 'Unknown hhea table version ' . $ver_maj );
			$this->skip ( 28 );
		} else {
			$this->skip ( 32 );
		}
		$metricDataFormat = $this->read_ushort ();
		if ($metricDataFormat != 0)
			die ( 'Unknown horizontal metric data format ' . $metricDataFormat );
		$numberOfHMetrics = $this->read_ushort ();
		if ($numberOfHMetrics == 0)
			die ( 'Number of horizontal metrics is 0' );
			
			// /////////////////////////////////
			// maxp - Maximum profile table
			// /////////////////////////////////
		$this->seek_table ( "maxp" );
		if ($debug) {
			$ver_maj = $this->read_ushort ();
			$ver_min = $this->read_ushort ();
			if ($ver_maj != 1)
				die ( 'Unknown maxp table version ' . $ver_maj );
		} else {
			$this->skip ( 4 );
		}
		$numGlyphs = $this->read_ushort ();
		
		// /////////////////////////////////
		// cmap - Character to glyph index mapping table
		// /////////////////////////////////
		/*
		 * Test purposes - displays list of cmaps $cmap_offset = $this->seek_table("cmap"); $this->skip(2); $cmapTableCount = $this->read_ushort(); $unicode_cmap_offset = 0; for ($i=0;$i<$cmapTableCount;$i++) { $x[$i]['platformId'] = $this->read_ushort(); $x[$i]['encodingId'] = $this->read_ushort(); $x[$i]['offset'] = $this->read_ulong(); $save_pos = $this->_pos; $x[$i]['format'] = $this->get_ushort($cmap_offset + $x[$i]['offset'] ); $this->seek($save_pos ); } print_r($x); exit;
		 */
		
		$cmap_offset = $this->seek_table ( "cmap" );
		$this->skip ( 2 );
		$cmapTableCount = $this->read_ushort ();
		$unicode_cmap_offset = 0;
		for($i = 0; $i < $cmapTableCount; $i ++) {
			$platformID = $this->read_ushort ();
			$encodingID = $this->read_ushort ();
			$offset = $this->read_ulong ();
			$save_pos = $this->_pos;
			if (($platformID == 3 && $encodingID == 1) || $platformID == 0) { // Microsoft,
			                                                                  // Unicode
				$format = $this->get_ushort ( $cmap_offset + $offset );
				if ($format == 4) {
					if (! $unicode_cmap_offset)
						$unicode_cmap_offset = $cmap_offset + $offset;
					if ($BMPonly)
						break; // mPDF 5.0
				}
			} 			// Microsoft, Unicode Format 12 table HKCS
			else if ((($platformID == 3 && $encodingID == 10) || $platformID == 0) && ! $BMPonly) {
				$format = $this->get_ushort ( $cmap_offset + $offset );
				if ($format == 12) {
					$unicode_cmap_offset = $cmap_offset + $offset;
					break;
				}
			}
			$this->seek ( $save_pos );
		}
		if (! $unicode_cmap_offset)
			die ( 'Font (' . $this->filename . ') does not have cmap for Unicode (platform 3, encoding 1, format 4, or platform 0, any encoding, format 4)' );
		
		$sipset = false;
		$smpset = false;
		// Format 12 CMAP does characters above Unicode BMP i.e. some HKCS
		// characters U+20000 and above
		if ($format == 12 && ! $BMPonly) {
			$this->maxUniChar = 0; // mPDF 5.0
			$this->seek ( $unicode_cmap_offset + 4 );
			$length = $this->read_ulong ();
			$limit = $unicode_cmap_offset + $length;
			$this->skip ( 4 );
			
			$nGroups = $this->read_ulong ();
			
			$glyphToChar = array ();
			$charToGlyph = array ();
			for($i = 0; $i < $nGroups; $i ++) {
				$startCharCode = $this->read_ulong ();
				$endCharCode = $this->read_ulong ();
				$startGlyphCode = $this->read_ulong ();
				if (($endCharCode > 0x20000 && $endCharCode < 0x2A6DF) || ($endCharCode > 0x2F800 && $endCharCode < 0x2FA1F)) {
					$sipset = true;
				} else if ($endCharCode > 0x10000 && $endCharCode < 0x1FFFF) {
					$smpset = true;
				}
				$offset = 0;
				for($unichar = $startCharCode; $unichar <= $endCharCode; $unichar ++) {
					$glyph = $startGlyphCode + $offset;
					$offset ++;
					$charToGlyph [$unichar] = $glyph;
					if ($unichar < 196608) {
						$this->maxUniChar = max ( $unichar, $this->maxUniChar );
					}
					$glyphToChar [$glyph] [] = $unichar;
				}
			}
		} else {
			
			$glyphToChar = array ();
			$charToGlyph = array ();
			$this->getCMAP4 ( $unicode_cmap_offset, $glyphToChar, $charToGlyph );
		}
		$this->sipset = $sipset;
		$this->smpset = $smpset;
		
		// /////////////////////////////////
		// hmtx - Horizontal metrics table
		// /////////////////////////////////
		$this->getHMTX ( $numberOfHMetrics, $numGlyphs, $glyphToChar, $scale );
		
		// /////////////////////////////////
		// kern - Kerning pair table mPDF 5.1
		// /////////////////////////////////
		if ($kerninfo) {
			// Recognises old form of Kerning table - as required by Windows -
			// Format 0 only
			$kern_offset = $this->seek_table ( "kern" );
			$version = $this->read_ushort ();
			$nTables = $this->read_ushort ();
			// subtable header
			$sversion = $this->read_ushort ();
			$slength = $this->read_ushort ();
			$scoverage = $this->read_ushort ();
			$format = $scoverage >> 8;
			if ($kern_offset && $version == 0 && $format == 0) {
				// Format 0
				$nPairs = $this->read_ushort ();
				$this->skip ( 6 );
				for($i = 0; $i < $nPairs; $i ++) {
					$left = $this->read_ushort ();
					$right = $this->read_ushort ();
					$val = $this->read_short ();
					if (count ( $glyphToChar [$left] ) == 1 && count ( $glyphToChar [$right] ) == 1) {
						if ($left != 32 && $right != 32) {
							$this->kerninfo [$glyphToChar [$left] [0]] [$glyphToChar [$right] [0]] = intval ( $val * $scale );
						}
					}
				}
			}
		}
	}
	
	// ///////////////////////////////////////////////////////////////////////////////////////
	function makeSubset($file, &$subset, $TTCfontID = 0, $debug = false) {
		$this->filename = $file;
		$this->fh = fopen ( $file, 'rb' ) or die ( 'Can\'t open file ' . $file );
		$this->_pos = 0;
		$this->charWidths = '';
		$this->glyphPos = array ();
		$this->charToGlyph = array ();
		$this->tables = array ();
		$this->otables = array ();
		$this->ascent = 0;
		$this->descent = 0;
		$this->numTTCFonts = 0;
		$this->TTCFonts = array ();
		$this->skip ( 4 );
		$this->maxUni = 0;
		if ($TTCfontID > 0) {
			$this->version = $version = $this->read_ulong (); // TTC Header version
			                                                  // now
			if (! in_array ( $version, array (
					0x00010000,
					0x00020000 
			) ))
				die ( "ERROR - Error parsing TrueType Collection: version=" . $version . " - " . $file );
			$this->numTTCFonts = $this->read_ulong ();
			for($i = 1; $i <= $this->numTTCFonts; $i ++) {
				$this->TTCFonts [$i] ['offset'] = $this->read_ulong ();
			}
			$this->seek ( $this->TTCFonts [$TTCfontID] ['offset'] );
			$this->version = $version = $this->read_ulong (); // TTFont version
				                                                  // again now
		}
		$this->readTableDirectory ( $debug );
		
		// /////////////////////////////////
		// head - Font header table
		// /////////////////////////////////
		$this->seek_table ( "head" );
		$this->skip ( 50 );
		$indexToLocFormat = $this->read_ushort ();
		$glyphDataFormat = $this->read_ushort ();
		
		// /////////////////////////////////
		// hhea - Horizontal header table
		// /////////////////////////////////
		$this->seek_table ( "hhea" );
		$this->skip ( 32 );
		$metricDataFormat = $this->read_ushort ();
		$orignHmetrics = $numberOfHMetrics = $this->read_ushort ();
		
		// /////////////////////////////////
		// maxp - Maximum profile table
		// /////////////////////////////////
		$this->seek_table ( "maxp" );
		$this->skip ( 4 );
		$numGlyphs = $this->read_ushort ();
		
		// /////////////////////////////////
		// cmap - Character to glyph index mapping table
		// /////////////////////////////////
		$cmap_offset = $this->seek_table ( "cmap" );
		$this->skip ( 2 );
		$cmapTableCount = $this->read_ushort ();
		$unicode_cmap_offset = 0;
		for($i = 0; $i < $cmapTableCount; $i ++) {
			$platformID = $this->read_ushort ();
			$encodingID = $this->read_ushort ();
			$offset = $this->read_ulong ();
			$save_pos = $this->_pos;
			if (($platformID == 3 && $encodingID == 1) || $platformID == 0) { // Microsoft,
			                                                                  // Unicode
				$format = $this->get_ushort ( $cmap_offset + $offset );
				if ($format == 4) {
					$unicode_cmap_offset = $cmap_offset + $offset;
					break;
				}
			}
			$this->seek ( $save_pos );
		}
		
		if (! $unicode_cmap_offset)
			die ( 'Font (' . $this->filename . ') does not have Unicode cmap (platform 3, encoding 1, format 4, or platform 0 [any encoding] format 4)' );
		
		$glyphToChar = array ();
		$charToGlyph = array ();
		$this->getCMAP4 ( $unicode_cmap_offset, $glyphToChar, $charToGlyph );
		
		$this->charToGlyph = $charToGlyph;
		
		// /////////////////////////////////
		// hmtx - Horizontal metrics table
		// /////////////////////////////////
		$scale = 1; // not used
		$this->getHMTX ( $numberOfHMetrics, $numGlyphs, $glyphToChar, $scale );
		
		// /////////////////////////////////
		// loca - Index to location
		// /////////////////////////////////
		$this->getLOCA ( $indexToLocFormat, $numGlyphs );
		
		$subsetglyphs = array (
				0 => 0,
				1 => 1,
				2 => 2 
		); // mPDF 5.3.31
		$subsetCharToGlyph = array ();
		foreach ( $subset as $code ) {
			if (isset ( $this->charToGlyph [$code] )) {
				$subsetglyphs [$this->charToGlyph [$code]] = $code; // Old Glyph ID
				                                                    // =>
				                                                    // Unicode
				$subsetCharToGlyph [$code] = $this->charToGlyph [$code]; // Unicode
					                                                         // to
					                                                         // old
					                                                         // GlyphID
			}
			$this->maxUni = max ( $this->maxUni, $code );
		}
		
		list ( $start, $dummy ) = $this->get_table_pos ( 'glyf' );
		
		$glyphSet = array ();
		ksort ( $subsetglyphs );
		$n = 0;
		// mPDF 5.0
		$fsLastCharIndex = 0; // maximum Unicode index (character code) in this
		                      // font, according to the cmap subtable for platform
		                      // ID
		                      // 3 and platform- specific encoding ID 0 or 1.
		foreach ( $subsetglyphs as $originalGlyphIdx => $uni ) {
			$fsLastCharIndex = max ( $fsLastCharIndex, $uni ); // mPDF 5.0
			$glyphSet [$originalGlyphIdx] = $n; // old glyphID to new glyphID
			$n ++;
		}
		
		ksort ( $subsetCharToGlyph );
		foreach ( $subsetCharToGlyph as $uni => $originalGlyphIdx ) {
			$codeToGlyph [$uni] = $glyphSet [$originalGlyphIdx];
		}
		$this->codeToGlyph = $codeToGlyph;
		
		ksort ( $subsetglyphs );
		foreach ( $subsetglyphs as $originalGlyphIdx => $uni ) {
			$this->getGlyphs ( $originalGlyphIdx, $start, $glyphSet, $subsetglyphs );
		}
		
		$numGlyphs = $numberOfHMetrics = count ( $subsetglyphs );
		
		// /////////////////////////////////
		// name - table copied from the original
		// /////////////////////////////////
		$this->add ( 'name', $this->get_table ( 'name' ) );
		
		// /////////////////////////////////
		// tables copied from the original
		// /////////////////////////////////
		// mPDF 5.0.067 // 5.1.020
		$tags = array (
				'cvt ',
				'fpgm',
				'prep',
				'gasp' 
		);
		foreach ( $tags as $tag ) {
			if (isset ( $this->tables [$tag] )) {
				$this->add ( $tag, $this->get_table ( $tag ) );
			}
		}
		
		// /////////////////////////////////
		// post - PostScript
		// /////////////////////////////////
		if (isset ( $this->tables ['post'] )) {
			$opost = $this->get_table ( 'post' );
			$post = "\x00\x03\x00\x00" . substr ( $opost, 4, 12 ) . "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
			$this->add ( 'post', $post );
		}
		
		// /////////////////////////////////
		// Sort CID2GID map into segments of contiguous codes
		// /////////////////////////////////
		ksort ( $codeToGlyph );
		unset ( $codeToGlyph [0] );
		// unset($codeToGlyph[65535]);
		$rangeid = 0;
		$range = array ();
		$prevcid = - 2;
		$prevglidx = - 1;
		// for each character
		foreach ( $codeToGlyph as $cid => $glidx ) {
			if ($cid == ($prevcid + 1) && $glidx == ($prevglidx + 1)) {
				$range [$rangeid] [] = $glidx;
			} else {
				// new range
				$rangeid = $cid;
				$range [$rangeid] = array ();
				$range [$rangeid] [] = $glidx;
			}
			$prevcid = $cid;
			$prevglidx = $glidx;
		}
		
		// /////////////////////////////////
		// CMap table
		// /////////////////////////////////
		// cmap - Character to glyph mapping
		$segCount = count ( $range ) + 1; // + 1 Last segment has missing
		                                  // character
		                                  // 0xFFFF
		$searchRange = 1;
		$entrySelector = 0;
		while ( $searchRange * 2 <= $segCount ) {
			$searchRange = $searchRange * 2;
			$entrySelector = $entrySelector + 1;
		}
		$searchRange = $searchRange * 2;
		$rangeShift = $segCount * 2 - $searchRange;
		$length = 16 + (8 * $segCount) + ($numGlyphs + 1);
		// mPDF 5.3.30
		$cmap = array (
				0,
				3, // Index : version, number of encoding subtables
				0,
				0, // Encoding Subtable : platform (UNI=0), encoding 0
				0,
				28, // Encoding Subtable : offset (hi,lo)
				0,
				3, // Encoding Subtable : platform (UNI=0), encoding 3
				0,
				28, // Encoding Subtable : offset (hi,lo)
				3,
				1, // Encoding Subtable : platform (MS=3), encoding 1
				0,
				28, // Encoding Subtable : offset (hi,lo)
				4,
				$length,
				0, // Format 4 Mapping subtable: format, length,
				   // language
				$segCount * 2,
				$searchRange,
				$entrySelector,
				$rangeShift 
		);
		
		// endCode(s)
		foreach ( $range as $start => $subrange ) {
			$endCode = $start + (count ( $subrange ) - 1);
			$cmap [] = $endCode; // endCode(s)
		}
		$cmap [] = 0xFFFF; // endCode of last Segment
		$cmap [] = 0; // reservedPad
		              
		// startCode(s)
		foreach ( $range as $start => $subrange ) {
			$cmap [] = $start; // startCode(s)
		}
		$cmap [] = 0xFFFF; // startCode of last Segment
		                   // idDelta(s)
		foreach ( $range as $start => $subrange ) {
			$idDelta = - ($start - $subrange [0]);
			$n += count ( $subrange );
			$cmap [] = $idDelta; // idDelta(s)
		}
		$cmap [] = 1; // idDelta of last Segment
		              // idRangeOffset(s)
		foreach ( $range as $subrange ) {
			$cmap [] = 0; // idRangeOffset[segCount] Offset in bytes to glyph
				              // indexArray, or 0
		}
		$cmap [] = 0; // idRangeOffset of last Segment
		foreach ( $range as $subrange ) {
			foreach ( $subrange as $glidx ) {
				$cmap [] = $glidx;
			}
		}
		$cmap [] = 0; // Mapping for last character
		$cmapstr = '';
		foreach ( $cmap as $cm ) {
			$cmapstr .= pack ( "n", $cm );
		}
		$this->add ( 'cmap', $cmapstr );
		
		// /////////////////////////////////
		// glyf - Glyph data
		// /////////////////////////////////
		list ( $glyfOffset, $glyfLength ) = $this->get_table_pos ( 'glyf' );
		if ($glyfLength < $this->maxStrLenRead) {
			$glyphData = $this->get_table ( 'glyf' );
		}
		
		$offsets = array ();
		$glyf = '';
		$pos = 0;
		$hmtxstr = '';
		$xMinT = 0;
		$yMinT = 0;
		$xMaxT = 0;
		$yMaxT = 0;
		$advanceWidthMax = 0;
		$minLeftSideBearing = 0;
		$minRightSideBearing = 0;
		$xMaxExtent = 0;
		$maxPoints = 0; // points in non-compound glyph
		$maxContours = 0; // contours in non-compound glyph
		$maxComponentPoints = 0; // points in compound glyph
		$maxComponentContours = 0; // contours in compound glyph
		$maxComponentElements = 0; // number of glyphs referenced at top level
		$maxComponentDepth = 0; // levels of recursion, set to 0 if font has only
		                        // simple glyphs
		$this->glyphdata = array ();
		
		foreach ( $subsetglyphs as $originalGlyphIdx => $uni ) {
			// hmtx - Horizontal Metrics
			$hm = $this->getHMetric ( $orignHmetrics, $originalGlyphIdx );
			$hmtxstr .= $hm;
			
			$offsets [] = $pos;
			$glyphPos = $this->glyphPos [$originalGlyphIdx];
			$glyphLen = $this->glyphPos [$originalGlyphIdx + 1] - $glyphPos;
			if ($glyfLength < $this->maxStrLenRead) {
				$data = substr ( $glyphData, $glyphPos, $glyphLen );
			} else {
				if ($glyphLen > 0)
					$data = $this->get_chunk ( $glyfOffset + $glyphPos, $glyphLen );
				else
					$data = '';
			}
			
			// mPDF 5.0
			if ($glyphLen > 0) {
				if (_RECALC_PROFILE) {
					$xMin = $this->unpack_short ( substr ( $data, 2, 2 ) );
					$yMin = $this->unpack_short ( substr ( $data, 4, 2 ) );
					$xMax = $this->unpack_short ( substr ( $data, 6, 2 ) );
					$yMax = $this->unpack_short ( substr ( $data, 8, 2 ) );
					$xMinT = min ( $xMinT, $xMin );
					$yMinT = min ( $yMinT, $yMin );
					$xMaxT = max ( $xMaxT, $xMax );
					$yMaxT = max ( $yMaxT, $yMax );
					$aw = $this->unpack_short ( substr ( $hm, 0, 2 ) );
					$lsb = $this->unpack_short ( substr ( $hm, 2, 2 ) );
					$advanceWidthMax = max ( $advanceWidthMax, $aw );
					$minLeftSideBearing = min ( $minLeftSideBearing, $lsb );
					$minRightSideBearing = min ( $minRightSideBearing, ($aw - $lsb - ($xMax - $xMin)) );
					$xMaxExtent = max ( $xMaxExtent, ($lsb + ($xMax - $xMin)) );
				}
				$up = unpack ( "n", substr ( $data, 0, 2 ) );
			}
			if ($glyphLen > 2 && ($up [1] & (1 << 15))) { // If number of contours
			                                              // <= -1 i.e. composiste
			                                              // glyph
				$pos_in_glyph = 10;
				$flags = GF_MORE;
				$nComponentElements = 0;
				while ( $flags & GF_MORE ) {
					$nComponentElements += 1; // number of glyphs referenced at top
					                          // level
					$up = unpack ( "n", substr ( $data, $pos_in_glyph, 2 ) );
					$flags = $up [1];
					$up = unpack ( "n", substr ( $data, $pos_in_glyph + 2, 2 ) );
					$glyphIdx = $up [1];
					$this->glyphdata [$originalGlyphIdx] ['compGlyphs'] [] = $glyphIdx;
					$data = $this->_set_ushort ( $data, $pos_in_glyph + 2, $glyphSet [$glyphIdx] );
					$pos_in_glyph += 4;
					if ($flags & GF_WORDS) {
						$pos_in_glyph += 4;
					} else {
						$pos_in_glyph += 2;
					}
					if ($flags & GF_SCALE) {
						$pos_in_glyph += 2;
					} else if ($flags & GF_XYSCALE) {
						$pos_in_glyph += 4;
					} else if ($flags & GF_TWOBYTWO) {
						$pos_in_glyph += 8;
					}
				}
				$maxComponentElements = max ( $maxComponentElements, $nComponentElements );
			} 			// mPDF 5.0 - Simple Glyph
			else if (_RECALC_PROFILE && $glyphLen > 2 && $up [1] < (1 << 15) && $up [1] > 0) { // Number
			                                                                                   // of
			                                                                                   // contours
			                                                                                   // >
			                                                                                   // 0
			                                                                                   // simple
			                                                                                   // glyph
				$nContours = $up [1];
				$this->glyphdata [$originalGlyphIdx] ['nContours'] = $nContours;
				$maxContours = max ( $maxContours, $nContours );
				
				// Count number of points in simple glyph
				$pos_in_glyph = 10 + ($nContours * 2) - 2; // Last endContourPoint
				$up = unpack ( "n", substr ( $data, $pos_in_glyph, 2 ) );
				$points = $up [1] + 1;
				$this->glyphdata [$originalGlyphIdx] ['nPoints'] = $points;
				$maxPoints = max ( $maxPoints, $points );
			}
			
			$glyf .= $data;
			$pos += $glyphLen;
			if ($pos % 4 != 0) {
				$padding = 4 - ($pos % 4);
				$glyf .= str_repeat ( "\0", $padding );
				$pos += $padding;
			}
		}
		
		if (_RECALC_PROFILE) {
			foreach ( $this->glyphdata as $originalGlyphIdx => $val ) {
				$maxdepth = $depth = - 1;
				$points = 0;
				$contours = 0;
				$this->getGlyphData ( $originalGlyphIdx, $maxdepth, $depth, $points, $contours );
				$maxComponentDepth = max ( $maxComponentDepth, $maxdepth );
				$maxComponentPoints = max ( $maxComponentPoints, $points );
				$maxComponentContours = max ( $maxComponentContours, $contours );
			}
		}
		
		$offsets [] = $pos;
		$this->add ( 'glyf', $glyf );
		
		// /////////////////////////////////
		// hmtx - Horizontal Metrics
		// /////////////////////////////////
		$this->add ( 'hmtx', $hmtxstr );
		
		// /////////////////////////////////
		// loca - Index to location
		// /////////////////////////////////
		$locastr = '';
		if ((($pos + 1) >> 1) > 0xFFFF) {
			$indexToLocFormat = 1; // long format
			foreach ( $offsets as $offset ) {
				$locastr .= pack ( "N", $offset );
			}
		} else {
			$indexToLocFormat = 0; // short format
			foreach ( $offsets as $offset ) {
				$locastr .= pack ( "n", ($offset / 2) );
			}
		}
		$this->add ( 'loca', $locastr );
		
		// /////////////////////////////////
		// head - Font header
		// /////////////////////////////////
		$head = $this->get_table ( 'head' );
		$head = $this->_set_ushort ( $head, 50, $indexToLocFormat );
		if (_RECALC_PROFILE) {
			$head = $this->_set_short ( $head, 36, $xMinT ); // for all glyph
			                                                 // bounding
			                                                 // boxes
			$head = $this->_set_short ( $head, 38, $yMinT ); // for all glyph
			                                                 // bounding
			                                                 // boxes
			$head = $this->_set_short ( $head, 40, $xMaxT ); // for all glyph
			                                                 // bounding
			                                                 // boxes
			$head = $this->_set_short ( $head, 42, $yMaxT ); // for all glyph
			                                                 // bounding
			                                                 // boxes
			                                                 // mPDF 5.3.33
			$head [17] = chr ( $head [17] & ~ (1 << 4) ); // Unset Bit 4 (as
				                                              // hdmx/LTSH
				                                              // tables not included)
		}
		$this->add ( 'head', $head );
		
		// /////////////////////////////////
		// hhea - Horizontal Header
		// /////////////////////////////////
		$hhea = $this->get_table ( 'hhea' );
		$hhea = $this->_set_ushort ( $hhea, 34, $numberOfHMetrics );
		if (_RECALC_PROFILE) {
			$hhea = $this->_set_ushort ( $hhea, 10, $advanceWidthMax );
			$hhea = $this->_set_short ( $hhea, 12, $minLeftSideBearing );
			$hhea = $this->_set_short ( $hhea, 14, $minRightSideBearing );
			$hhea = $this->_set_short ( $hhea, 16, $xMaxExtent );
		}
		$this->add ( 'hhea', $hhea );
		
		// /////////////////////////////////
		// maxp - Maximum Profile
		// /////////////////////////////////
		$maxp = $this->get_table ( 'maxp' );
		$maxp = $this->_set_ushort ( $maxp, 4, $numGlyphs );
		if (_RECALC_PROFILE) {
			$maxp = $this->_set_ushort ( $maxp, 6, $maxPoints ); // points in
			                                                     // non-compound
			                                                     // glyph
			$maxp = $this->_set_ushort ( $maxp, 8, $maxContours ); // contours in
			                                                       // non-compound
			                                                       // glyph
			$maxp = $this->_set_ushort ( $maxp, 10, $maxComponentPoints ); // points
			                                                               // in
			                                                               // compound
			                                                               // glyph
			$maxp = $this->_set_ushort ( $maxp, 12, $maxComponentContours ); // contours
			                                                                 // in
			                                                                 // compound
			                                                                 // glyph
			$maxp = $this->_set_ushort ( $maxp, 28, $maxComponentElements ); // number
			                                                                 // of
			                                                                 // glyphs
			                                                                 // referenced
			                                                                 // at
			                                                                 // top
			                                                                 // level
			$maxp = $this->_set_ushort ( $maxp, 30, $maxComponentDepth ); // levels
				                                                              // of
				                                                              // recursion,
				                                                              // set
				                                                              // to 0
				                                                              // if
				                                                              // font
				                                                              // has
				                                                              // only
				                                                              // simple
				                                                              // glyphs
		}
		$this->add ( 'maxp', $maxp );
		
		// /////////////////////////////////
		// OS/2 - OS/2
		// /////////////////////////////////
		if (isset ( $this->tables ['OS/2'] )) {
			// mPDF 5.2.03
			$os2_offset = $this->seek_table ( "OS/2" );
			// mPDF 5.0
			if (_RECALC_PROFILE) {
				$fsSelection = $this->get_ushort ( $os2_offset + 62 );
				$fsSelection = ($fsSelection & ~ (1 << 6)); // 2-byte bit field
					                                            // containing
					                                            // information
					                                            // concerning the nature
					                                            // of the
					                                            // font patterns
					                                            // bit#0 = Italic;
					                                            // bit#5=Bold
					                                            // Match name table's
					                                            // font subfamily string
					                                            // Clear bit#6 used for
					                                            // 'Regular' and
					                                            // optional
			}
			
			// NB Currently this method never subsets characters above BMP
			// Could set nonBMP bit according to $this->maxUni
			$nonBMP = $this->get_ushort ( $os2_offset + 46 );
			$nonBMP = ($nonBMP & ~ (1 << 9)); // Unset Bit 57 (indicates non-BMP) -
			                                  // for interactive forms
			
			$os2 = $this->get_table ( 'OS/2' );
			if (_RECALC_PROFILE) {
				$os2 = $this->_set_ushort ( $os2, 62, $fsSelection );
				// mPDF 5.2.03
				$os2 = $this->_set_ushort ( $os2, 66, $fsLastCharIndex );
				$os2 = $this->_set_ushort ( $os2, 42, 0x0000 ); // ulCharRange
				                                                // (ulUnicodeRange)
				                                                // bits 24-31
				                                                // | 16-23
				$os2 = $this->_set_ushort ( $os2, 44, 0x0000 ); // ulCharRange
				                                                // (Unicode
				                                                // ranges) bits 8-15
				                                                // | 0-7
				$os2 = $this->_set_ushort ( $os2, 46, $nonBMP ); // ulCharRange
				                                                 // (Unicode ranges)
				                                                 // bits 56-63
				                                                 // | 48-55
				$os2 = $this->_set_ushort ( $os2, 48, 0x0000 ); // ulCharRange
				                                                // (Unicode
				                                                // ranges) bits
				                                                // 40-47 | 32-39
				$os2 = $this->_set_ushort ( $os2, 50, 0x0000 ); // ulCharRange
				                                                // (Unicode
				                                                // ranges) bits
				                                                // 88-95 | 80-87
				$os2 = $this->_set_ushort ( $os2, 52, 0x0000 ); // ulCharRange
				                                                // (Unicode
				                                                // ranges) bits
				                                                // 72-79 | 64-71
				$os2 = $this->_set_ushort ( $os2, 54, 0x0000 ); // ulCharRange
				                                                // (Unicode
				                                                // ranges) bits
				                                                // 120-127 |
				                                                // 112-119
				$os2 = $this->_set_ushort ( $os2, 56, 0x0000 ); // ulCharRange
					                                                // (Unicode
					                                                // ranges) bits
					                                                // 104-111 |
					                                                // 96-103
			}
			$os2 = $this->_set_ushort ( $os2, 46, $nonBMP ); // Unset Bit 57
			                                                 // (indicates non-BMP)
			                                                 // - for
			                                                 // interactive forms
			
			$this->add ( 'OS/2', $os2 );
		}
		
		fclose ( $this->fh );
		// Put the TTF file together
		$stm = '';
		$this->endTTFile ( $stm );
		// file_put_contents('testfont.ttf', $stm); exit;
		return $stm;
	}
	
	// ================================================================================
	
	// Also does SMP
	function makeSubsetSIP($file, &$subset, $TTCfontID = 0, $debug = false) {
		$this->fh = fopen ( $file, 'rb' ) or die ( 'Can\'t open file ' . $file );
		$this->filename = $file;
		$this->_pos = 0;
		$this->charWidths = '';
		$this->glyphPos = array ();
		$this->charToGlyph = array ();
		$this->tables = array ();
		$this->otables = array ();
		$this->ascent = 0;
		$this->descent = 0;
		$this->numTTCFonts = 0;
		$this->TTCFonts = array ();
		$this->skip ( 4 );
		if ($TTCfontID > 0) {
			$this->version = $version = $this->read_ulong (); // TTC Header version
			                                                  // now
			if (! in_array ( $version, array (
					0x00010000,
					0x00020000 
			) ))
				die ( "ERROR - Error parsing TrueType Collection: version=" . $version . " - " . $file );
			$this->numTTCFonts = $this->read_ulong ();
			for($i = 1; $i <= $this->numTTCFonts; $i ++) {
				$this->TTCFonts [$i] ['offset'] = $this->read_ulong ();
			}
			$this->seek ( $this->TTCFonts [$TTCfontID] ['offset'] );
			$this->version = $version = $this->read_ulong (); // TTFont version
				                                                  // again now
		}
		$this->readTableDirectory ( $debug );
		
		// /////////////////////////////////
		// head - Font header table
		// /////////////////////////////////
		$this->seek_table ( "head" );
		$this->skip ( 50 );
		$indexToLocFormat = $this->read_ushort ();
		$glyphDataFormat = $this->read_ushort ();
		
		// /////////////////////////////////
		// hhea - Horizontal header table
		// /////////////////////////////////
		$this->seek_table ( "hhea" );
		$this->skip ( 32 );
		$metricDataFormat = $this->read_ushort ();
		$orignHmetrics = $numberOfHMetrics = $this->read_ushort ();
		
		// /////////////////////////////////
		// maxp - Maximum profile table
		// /////////////////////////////////
		$this->seek_table ( "maxp" );
		$this->skip ( 4 );
		$numGlyphs = $this->read_ushort ();
		
		// /////////////////////////////////
		// cmap - Character to glyph index mapping table
		// /////////////////////////////////
		
		$cmap_offset = $this->seek_table ( "cmap" );
		$this->skip ( 2 );
		$cmapTableCount = $this->read_ushort ();
		$unicode_cmap_offset = 0;
		for($i = 0; $i < $cmapTableCount; $i ++) {
			$platformID = $this->read_ushort ();
			$encodingID = $this->read_ushort ();
			$offset = $this->read_ulong ();
			$save_pos = $this->_pos;
			// mPDF 5.3.30
			if (($platformID == 3 && $encodingID == 10) || $platformID == 0) { // Microsoft,
			                                                                   // Unicode
			                                                                   // Format
			                                                                   // 12
			                                                                   // table
			                                                                   // HKCS
				$format = $this->get_ushort ( $cmap_offset + $offset );
				if ($format == 12) {
					$unicode_cmap_offset = $cmap_offset + $offset;
					break;
				}
			}
			$this->seek ( $save_pos );
		}
		
		if (! $unicode_cmap_offset)
			die ( 'Font does not have cmap for Unicode (platform 3, encoding 1, format 4, or platform 0, any encoding, format 4)' );
			// Format 12 CMAP does characters above Unicode BMP i.e. some HKCS
			// characters U+20000 and above
		if ($format == 12) {
			$this->maxUniChar = 0;
			$this->seek ( $unicode_cmap_offset + 4 );
			$length = $this->read_ulong ();
			$limit = $unicode_cmap_offset + $length;
			$this->skip ( 4 );
			
			$nGroups = $this->read_ulong ();
			
			$glyphToChar = array ();
			$charToGlyph = array ();
			for($i = 0; $i < $nGroups; $i ++) {
				$startCharCode = $this->read_ulong ();
				$endCharCode = $this->read_ulong ();
				$startGlyphCode = $this->read_ulong ();
				$offset = 0;
				for($unichar = $startCharCode; $unichar <= $endCharCode; $unichar ++) {
					$glyph = $startGlyphCode + $offset;
					$offset ++;
					$charToGlyph [$unichar] = $glyph;
					if ($unichar < 196608) {
						$this->maxUniChar = max ( $unichar, $this->maxUniChar );
					}
					$glyphToChar [$glyph] [] = $unichar;
				}
			}
		} else
			die ( 'Font does not have cmap for Unicode (format 12)' );
			
			// /////////////////////////////////
			// hmtx - Horizontal metrics table
			// /////////////////////////////////
		$scale = 1; // not used here
		$this->getHMTX ( $numberOfHMetrics, $numGlyphs, $glyphToChar, $scale );
		
		// /////////////////////////////////
		// loca - Index to location
		// /////////////////////////////////
		$this->getLOCA ( $indexToLocFormat, $numGlyphs );
		
		// /////////////////////////////////////////////////////////////////
		
		$glyphMap = array (
				0 => 0 
		);
		$glyphSet = array (
				0 => 0 
		);
		$codeToGlyph = array ();
		// mPDF 5.0.067 Set a substitute if ASCII characters do not have glyphs
		if (isset ( $charToGlyph [0x3F] )) {
			$subs = $charToGlyph [0x3F];
		} 		// Question
		  // mark
		else {
			$subs = $charToGlyph [32];
		}
		foreach ( $subset as $code ) {
			if (isset ( $charToGlyph [$code] ))
				$originalGlyphIdx = $charToGlyph [$code];
			else if ($code < 128) { // mPDF 5.0.067
				$originalGlyphIdx = $subs;
			} else {
				$originalGlyphIdx = 0;
			}
			if (! isset ( $glyphSet [$originalGlyphIdx] )) {
				$glyphSet [$originalGlyphIdx] = count ( $glyphMap );
				$glyphMap [] = $originalGlyphIdx;
			}
			$codeToGlyph [$code] = $glyphSet [$originalGlyphIdx];
		}
		
		list ( $start, $dummy ) = $this->get_table_pos ( 'glyf' );
		
		$n = 0;
		while ( $n < count ( $glyphMap ) ) {
			$originalGlyphIdx = $glyphMap [$n];
			$glyphPos = $this->glyphPos [$originalGlyphIdx];
			$glyphLen = $this->glyphPos [$originalGlyphIdx + 1] - $glyphPos;
			$n += 1;
			if (! $glyphLen)
				continue;
			$this->seek ( $start + $glyphPos );
			$numberOfContours = $this->read_short ();
			if ($numberOfContours < 0) {
				$this->skip ( 8 );
				$flags = GF_MORE;
				while ( $flags & GF_MORE ) {
					$flags = $this->read_ushort ();
					$glyphIdx = $this->read_ushort ();
					if (! isset ( $glyphSet [$glyphIdx] )) {
						$glyphSet [$glyphIdx] = count ( $glyphMap );
						$glyphMap [] = $glyphIdx;
					}
					if ($flags & GF_WORDS)
						$this->skip ( 4 );
					else
						$this->skip ( 2 );
					if ($flags & GF_SCALE)
						$this->skip ( 2 );
					else if ($flags & GF_XYSCALE)
						$this->skip ( 4 );
					else if ($flags & GF_TWOBYTWO)
						$this->skip ( 8 );
				}
			}
		}
		
		$numGlyphs = $n = count ( $glyphMap );
		$numberOfHMetrics = $n;
		
		// /////////////////////////////////
		// name
		// /////////////////////////////////
		// Needs to have a name entry in 3,0 (e.g. symbol) - original font will
		// be 3,1 (i.e. Unicode)
		$name = $this->get_table ( 'name' );
		$name_offset = $this->seek_table ( "name" );
		$format = $this->read_ushort ();
		$numRecords = $this->read_ushort ();
		$string_data_offset = $name_offset + $this->read_ushort ();
		for($i = 0; $i < $numRecords; $i ++) {
			$platformId = $this->read_ushort ();
			$encodingId = $this->read_ushort ();
			if ($platformId == 3 && $encodingId == 1) {
				$pos = 6 + ($i * 12) + 2;
				$name = $this->_set_ushort ( $name, $pos, 0x00 ); // Change encoding
					                                                  // to
					                                                  // 3,0 rather than
					                                                  // 3,1
			}
			$this->skip ( 8 );
		}
		$this->add ( 'name', $name );
		
		// /////////////////////////////////
		// OS/2
		// /////////////////////////////////
		if (isset ( $this->tables ['OS/2'] )) {
			$os2 = $this->get_table ( 'OS/2' );
			$os2 = $this->_set_ushort ( $os2, 42, 0x00 ); // ulCharRange (Unicode
			                                              // ranges)
			$os2 = $this->_set_ushort ( $os2, 44, 0x00 ); // ulCharRange (Unicode
			                                              // ranges)
			$os2 = $this->_set_ushort ( $os2, 46, 0x00 ); // ulCharRange (Unicode
			                                              // ranges)
			$os2 = $this->_set_ushort ( $os2, 48, 0x00 ); // ulCharRange (Unicode
			                                              // ranges)
			
			$os2 = $this->_set_ushort ( $os2, 50, 0x00 ); // ulCharRange (Unicode
			                                              // ranges)
			$os2 = $this->_set_ushort ( $os2, 52, 0x00 ); // ulCharRange (Unicode
			                                              // ranges)
			$os2 = $this->_set_ushort ( $os2, 54, 0x00 ); // ulCharRange (Unicode
			                                              // ranges)
			$os2 = $this->_set_ushort ( $os2, 56, 0x00 ); // ulCharRange (Unicode
			                                              // ranges)
			                                              // Set Symbol character
			                                              // only
			                                              // in ulCodePageRange
			$os2 = $this->_set_ushort ( $os2, 78, 0x8000 ); // ulCodePageRange =
			                                                // Bit
			                                                // #31 Symbol **** 78 =
			                                                // Bit
			                                                // 16-31
			$os2 = $this->_set_ushort ( $os2, 80, 0x0000 ); // ulCodePageRange =
			                                                // Bit
			                                                // #31 Symbol **** 80 =
			                                                // Bit 0-15
			$os2 = $this->_set_ushort ( $os2, 82, 0x0000 ); // ulCodePageRange =
			                                                // Bit
			                                                // #32- Symbol **** 82
			                                                // = Bits
			                                                // 48-63
			$os2 = $this->_set_ushort ( $os2, 84, 0x0000 ); // ulCodePageRange =
			                                                // Bit
			                                                // #32- Symbol **** 84
			                                                // = Bits
			                                                // 32-47
			
			$os2 = $this->_set_ushort ( $os2, 64, 0x01 ); // FirstCharIndex
			$os2 = $this->_set_ushort ( $os2, 66, count ( $subset ) ); // LastCharIndex
			                                                           // Set
			                                                           // PANOSE
			                                                           // first bit
			                                                           // to 5 for
			                                                           // Symbol
			                                                           // mPDF
			                                                           // 5.0.017
			$os2 = $this->splice ( $os2, 32, chr ( 5 ) . chr ( 0 ) . chr ( 1 ) . chr ( 0 ) . chr ( 1 ) . chr ( 0 ) . chr ( 0 ) . chr ( 0 ) . chr ( 0 ) . chr ( 0 ) );
			$this->add ( 'OS/2', $os2 );
		}
		
		// /////////////////////////////////
		// tables copied from the original
		// /////////////////////////////////
		// mPDF 5.1.020 // mPDF 5.1.022
		$tags = array (
				'cvt ',
				'fpgm',
				'prep',
				'gasp' 
		);
		foreach ( $tags as $tag ) { // 1.02
			if (isset ( $this->tables [$tag] )) {
				$this->add ( $tag, $this->get_table ( $tag ) );
			}
		}
		
		// /////////////////////////////////
		// post - PostScript
		// /////////////////////////////////
		if (isset ( $this->tables ['post'] )) {
			$opost = $this->get_table ( 'post' );
			$post = "\x00\x03\x00\x00" . substr ( $opost, 4, 12 ) . "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
		}
		$this->add ( 'post', $post );
		
		// /////////////////////////////////
		// hhea - Horizontal Header
		// /////////////////////////////////
		$hhea = $this->get_table ( 'hhea' );
		$hhea = $this->_set_ushort ( $hhea, 34, $numberOfHMetrics );
		$this->add ( 'hhea', $hhea );
		
		// /////////////////////////////////
		// maxp - Maximum Profile
		// /////////////////////////////////
		$maxp = $this->get_table ( 'maxp' );
		$maxp = $this->_set_ushort ( $maxp, 4, $numGlyphs );
		$this->add ( 'maxp', $maxp );
		
		// /////////////////////////////////
		// CMap table Formats [1,0,]6 and [3,0,]4
		// /////////////////////////////////
		// /////////////////////////////////
		// Sort CID2GID map into segments of contiguous codes
		// /////////////////////////////////
		$rangeid = 0;
		$range = array ();
		$prevcid = - 2;
		$prevglidx = - 1;
		// for each character
		foreach ( $subset as $cid => $code ) {
			$glidx = $codeToGlyph [$code];
			if ($cid == ($prevcid + 1) && $glidx == ($prevglidx + 1)) {
				$range [$rangeid] [] = $glidx;
			} else {
				// new range
				$rangeid = $cid;
				$range [$rangeid] = array ();
				$range [$rangeid] [] = $glidx;
			}
			$prevcid = $cid;
			$prevglidx = $glidx;
		}
		// cmap - Character to glyph mapping
		$segCount = count ( $range ) + 1; // + 1 Last segment has missing
		                                  // character
		                                  // 0xFFFF
		$searchRange = 1;
		$entrySelector = 0;
		while ( $searchRange * 2 <= $segCount ) {
			$searchRange = $searchRange * 2;
			$entrySelector = $entrySelector + 1;
		}
		$searchRange = $searchRange * 2;
		$rangeShift = $segCount * 2 - $searchRange;
		$length = 16 + (8 * $segCount) + ($numGlyphs + 1);
		$cmap = array (
				4,
				$length,
				0, // Format 4 Mapping subtable: format, length,
				   // language
				$segCount * 2,
				$searchRange,
				$entrySelector,
				$rangeShift 
		);
		
		// endCode(s)
		foreach ( $range as $start => $subrange ) {
			$endCode = $start + (count ( $subrange ) - 1);
			$cmap [] = $endCode; // endCode(s)
		}
		$cmap [] = 0xFFFF; // endCode of last Segment
		$cmap [] = 0; // reservedPad
		              
		// startCode(s)
		foreach ( $range as $start => $subrange ) {
			$cmap [] = $start; // startCode(s)
		}
		$cmap [] = 0xFFFF; // startCode of last Segment
		                   // idDelta(s)
		foreach ( $range as $start => $subrange ) {
			$idDelta = - ($start - $subrange [0]);
			$n += count ( $subrange );
			$cmap [] = $idDelta; // idDelta(s)
		}
		$cmap [] = 1; // idDelta of last Segment
		              // idRangeOffset(s)
		foreach ( $range as $subrange ) {
			$cmap [] = 0; // idRangeOffset[segCount] Offset in bytes to glyph
				              // indexArray, or 0
		}
		$cmap [] = 0; // idRangeOffset of last Segment
		foreach ( $range as $subrange ) {
			foreach ( $subrange as $glidx ) {
				$cmap [] = $glidx;
			}
		}
		$cmap [] = 0; // Mapping for last character
		$cmapstr4 = '';
		foreach ( $cmap as $cm ) {
			$cmapstr4 .= pack ( "n", $cm );
		}
		
		// /////////////////////////////////
		// cmap - Character to glyph mapping
		// /////////////////////////////////
		$entryCount = count ( $subset );
		$length = 10 + $entryCount * 2;
		
		$off = 20 + $length;
		$hoff = $off >> 16;
		$loff = $off & 0xFFFF;
		
		$cmap = array (
				0,
				2, // Index : version, number of subtables
				1,
				0, // Subtable : platform, encoding
				0,
				20, // offset (hi,lo)
				3,
				0, // Subtable : platform, encoding
				$hoff,
				$loff, // offset (hi,lo)
				6,
				$length, // Format 6 Mapping table: format, length
				0,
				1, // language, First char code
				$entryCount 
		);
		$cmapstr = '';
		foreach ( $subset as $code ) {
			$cmap [] = $codeToGlyph [$code];
		}
		foreach ( $cmap as $cm ) {
			$cmapstr .= pack ( "n", $cm );
		}
		$cmapstr .= $cmapstr4;
		$this->add ( 'cmap', $cmapstr );
		
		// /////////////////////////////////
		// hmtx - Horizontal Metrics
		// /////////////////////////////////
		$hmtxstr = '';
		for($n = 0; $n < $numGlyphs; $n ++) {
			$originalGlyphIdx = $glyphMap [$n];
			$hm = $this->getHMetric ( $orignHmetrics, $originalGlyphIdx );
			$hmtxstr .= $hm;
		}
		$this->add ( 'hmtx', $hmtxstr );
		
		// /////////////////////////////////
		// glyf - Glyph data
		// /////////////////////////////////
		list ( $glyfOffset, $glyfLength ) = $this->get_table_pos ( 'glyf' );
		if ($glyfLength < $this->maxStrLenRead) {
			$glyphData = $this->get_table ( 'glyf' );
		}
		
		$offsets = array ();
		$glyf = '';
		$pos = 0;
		for($n = 0; $n < $numGlyphs; $n ++) {
			$offsets [] = $pos;
			$originalGlyphIdx = $glyphMap [$n];
			$glyphPos = $this->glyphPos [$originalGlyphIdx];
			$glyphLen = $this->glyphPos [$originalGlyphIdx + 1] - $glyphPos;
			if ($glyfLength < $this->maxStrLenRead) {
				$data = substr ( $glyphData, $glyphPos, $glyphLen );
			} else {
				if ($glyphLen > 0)
					$data = $this->get_chunk ( $glyfOffset + $glyphPos, $glyphLen );
				else
					$data = '';
			}
			if ($glyphLen > 0)
				$up = unpack ( "n", substr ( $data, 0, 2 ) );
			if ($glyphLen > 2 && ($up [1] & (1 << 15))) {
				$pos_in_glyph = 10;
				$flags = GF_MORE;
				while ( $flags & GF_MORE ) {
					$up = unpack ( "n", substr ( $data, $pos_in_glyph, 2 ) );
					$flags = $up [1];
					$up = unpack ( "n", substr ( $data, $pos_in_glyph + 2, 2 ) );
					$glyphIdx = $up [1];
					$data = $this->_set_ushort ( $data, $pos_in_glyph + 2, $glyphSet [$glyphIdx] );
					$pos_in_glyph += 4;
					if ($flags & GF_WORDS) {
						$pos_in_glyph += 4;
					} else {
						$pos_in_glyph += 2;
					}
					if ($flags & GF_SCALE) {
						$pos_in_glyph += 2;
					} else if ($flags & GF_XYSCALE) {
						$pos_in_glyph += 4;
					} else if ($flags & GF_TWOBYTWO) {
						$pos_in_glyph += 8;
					}
				}
			}
			$glyf .= $data;
			$pos += $glyphLen;
			if ($pos % 4 != 0) {
				$padding = 4 - ($pos % 4);
				$glyf .= str_repeat ( "\0", $padding );
				$pos += $padding;
			}
		}
		$offsets [] = $pos;
		$this->add ( 'glyf', $glyf );
		
		// /////////////////////////////////
		// loca - Index to location
		// /////////////////////////////////
		$locastr = '';
		if ((($pos + 1) >> 1) > 0xFFFF) {
			$indexToLocFormat = 1; // long format
			foreach ( $offsets as $offset ) {
				$locastr .= pack ( "N", $offset );
			}
		} else {
			$indexToLocFormat = 0; // short format
			foreach ( $offsets as $offset ) {
				$locastr .= pack ( "n", ($offset / 2) );
			}
		}
		$this->add ( 'loca', $locastr );
		
		// /////////////////////////////////
		// head - Font header
		// /////////////////////////////////
		$head = $this->get_table ( 'head' );
		$head = $this->_set_ushort ( $head, 50, $indexToLocFormat );
		$this->add ( 'head', $head );
		
		fclose ( $this->fh );
		
		// Put the TTF file together
		$stm = '';
		$this->endTTFile ( $stm );
		// file_put_contents('testfont.ttf', $stm); exit;
		return $stm;
	}
	
	// ////////////////////////////////////////////////////////////////////////////////
	// Recursively get composite glyph data
	function getGlyphData($originalGlyphIdx, &$maxdepth, &$depth, &$points, &$contours) {
		$depth ++;
		$maxdepth = max ( $maxdepth, $depth );
		if (count ( $this->glyphdata [$originalGlyphIdx] ['compGlyphs'] )) {
			foreach ( $this->glyphdata [$originalGlyphIdx] ['compGlyphs'] as $glyphIdx ) {
				$this->getGlyphData ( $glyphIdx, $maxdepth, $depth, $points, $contours );
			}
		} else if (($this->glyphdata [$originalGlyphIdx] ['nContours'] > 0) && $depth > 0) { // simple
			$contours += $this->glyphdata [$originalGlyphIdx] ['nContours'];
			$points += $this->glyphdata [$originalGlyphIdx] ['nPoints'];
		}
		$depth --;
	}
	
	// ////////////////////////////////////////////////////////////////////////////////
	// Recursively get composite glyphs
	function getGlyphs($originalGlyphIdx, &$start, &$glyphSet, &$subsetglyphs) {
		$glyphPos = $this->glyphPos [$originalGlyphIdx];
		$glyphLen = $this->glyphPos [$originalGlyphIdx + 1] - $glyphPos;
		if (! $glyphLen) {
			return;
		}
		$this->seek ( $start + $glyphPos );
		$numberOfContours = $this->read_short ();
		if ($numberOfContours < 0) {
			$this->skip ( 8 );
			$flags = GF_MORE;
			while ( $flags & GF_MORE ) {
				$flags = $this->read_ushort ();
				$glyphIdx = $this->read_ushort ();
				if (! isset ( $glyphSet [$glyphIdx] )) {
					$glyphSet [$glyphIdx] = count ( $subsetglyphs ); // old glyphID
					                                                 // to
					                                                 // new glyphID
					$subsetglyphs [$glyphIdx] = true;
				}
				$savepos = ftell ( $this->fh );
				$this->getGlyphs ( $glyphIdx, $start, $glyphSet, $subsetglyphs );
				$this->seek ( $savepos );
				if ($flags & GF_WORDS)
					$this->skip ( 4 );
				else
					$this->skip ( 2 );
				if ($flags & GF_SCALE)
					$this->skip ( 2 );
				else if ($flags & GF_XYSCALE)
					$this->skip ( 4 );
				else if ($flags & GF_TWOBYTWO)
					$this->skip ( 8 );
			}
		}
	}
	
	// ////////////////////////////////////////////////////////////////////////////////
	function getHMTX($numberOfHMetrics, $numGlyphs, &$glyphToChar, $scale) {
		$start = $this->seek_table ( "hmtx" );
		$aw = 0;
		$this->charWidths = str_pad ( '', 256 * 256 * 2, "\x00" );
		if ($this->maxUniChar > 65536) {
			$this->charWidths .= str_pad ( '', 256 * 256 * 2, "\x00" );
		} // Plane
		  // 1
		  // SMP
		if ($this->maxUniChar > 131072) {
			$this->charWidths .= str_pad ( '', 256 * 256 * 2, "\x00" );
		} // Plane
		  // 2
		  // SMP
		$nCharWidths = 0;
		if (($numberOfHMetrics * 4) < $this->maxStrLenRead) {
			$data = $this->get_chunk ( $start, ($numberOfHMetrics * 4) );
			$arr = unpack ( "n*", $data );
		} else {
			$this->seek ( $start );
		}
		for($glyph = 0; $glyph < $numberOfHMetrics; $glyph ++) {
			if (($numberOfHMetrics * 4) < $this->maxStrLenRead) {
				$aw = $arr [($glyph * 2) + 1];
			} else {
				$aw = $this->read_ushort ();
				$lsb = $this->read_ushort ();
			}
			if (isset ( $glyphToChar [$glyph] ) || $glyph == 0) {
				
				if ($aw >= (1 << 15)) {
					$aw = 0;
				} // 1.03 Some (arabic) fonts have
				  // -ve values for width
				  // although should be unsigned
				  // value - comes out as e.g. 65108
				  // (intended -50)
				if ($glyph == 0) {
					$this->defaultWidth = $scale * $aw;
					continue;
				}
				foreach ( $glyphToChar [$glyph] as $char ) {
					// $this->charWidths[$char] = intval(round($scale*$aw));
					if ($char != 0 && $char != 65535) {
						$w = intval ( round ( $scale * $aw ) );
						if ($w == 0) {
							$w = 65535;
						}
						if ($char < 196608) {
							$this->charWidths [$char * 2] = chr ( $w >> 8 );
							$this->charWidths [$char * 2 + 1] = chr ( $w & 0xFF );
							$nCharWidths ++;
						}
					}
				}
			}
		}
		$data = $this->get_chunk ( ($start + $numberOfHMetrics * 4), ($numGlyphs * 2) );
		$arr = unpack ( "n*", $data );
		$diff = $numGlyphs - $numberOfHMetrics;
		$w = intval ( round ( $scale * $aw ) ); // mPDF 5.3.32
		if ($w == 0) {
			$w = 65535;
		} // mPDF 5.3.32
		for($pos = 0; $pos < $diff; $pos ++) {
			$glyph = $pos + $numberOfHMetrics;
			if (isset ( $glyphToChar [$glyph] )) {
				foreach ( $glyphToChar [$glyph] as $char ) {
					if ($char != 0 && $char != 65535) {
						if ($char < 196608) {
							$this->charWidths [$char * 2] = chr ( $w >> 8 );
							$this->charWidths [$char * 2 + 1] = chr ( $w & 0xFF );
							$nCharWidths ++;
						}
					}
				}
			}
		}
		// NB 65535 is a set width of 0
		// First bytes define number of chars in font
		$this->charWidths [0] = chr ( $nCharWidths >> 8 );
		$this->charWidths [1] = chr ( $nCharWidths & 0xFF );
	}
	function getHMetric($numberOfHMetrics, $gid) {
		$start = $this->seek_table ( "hmtx" );
		if ($gid < $numberOfHMetrics) {
			$this->seek ( $start + ($gid * 4) );
			$hm = fread ( $this->fh, 4 );
		} else {
			$this->seek ( $start + (($numberOfHMetrics - 1) * 4) );
			$hm = fread ( $this->fh, 2 );
			$this->seek ( $start + ($numberOfHMetrics * 2) + ($gid * 2) );
			$hm .= fread ( $this->fh, 2 );
		}
		return $hm;
	}
	function getLOCA($indexToLocFormat, $numGlyphs) {
		$start = $this->seek_table ( 'loca' );
		$this->glyphPos = array ();
		if ($indexToLocFormat == 0) {
			$data = $this->get_chunk ( $start, ($numGlyphs * 2) + 2 );
			$arr = unpack ( "n*", $data );
			for($n = 0; $n <= $numGlyphs; $n ++) {
				$this->glyphPos [] = ($arr [$n + 1] * 2);
			}
		} else if ($indexToLocFormat == 1) {
			$data = $this->get_chunk ( $start, ($numGlyphs * 4) + 4 );
			$arr = unpack ( "N*", $data );
			for($n = 0; $n <= $numGlyphs; $n ++) {
				$this->glyphPos [] = ($arr [$n + 1]);
			}
		} else
			die ( 'Unknown location table format ' . $indexToLocFormat );
	}
	
	// CMAP Format 4
	function getCMAP4($unicode_cmap_offset, &$glyphToChar, &$charToGlyph) {
		$this->maxUniChar = 0;
		$this->seek ( $unicode_cmap_offset + 2 );
		$length = $this->read_ushort ();
		$limit = $unicode_cmap_offset + $length;
		$this->skip ( 2 );
		
		$segCount = $this->read_ushort () / 2;
		$this->skip ( 6 );
		$endCount = array ();
		for($i = 0; $i < $segCount; $i ++) {
			$endCount [] = $this->read_ushort ();
		}
		$this->skip ( 2 );
		$startCount = array ();
		for($i = 0; $i < $segCount; $i ++) {
			$startCount [] = $this->read_ushort ();
		}
		$idDelta = array ();
		for($i = 0; $i < $segCount; $i ++) {
			$idDelta [] = $this->read_short ();
		} // ????
		  // was
		  // unsigned
		  // short
		$idRangeOffset_start = $this->_pos;
		$idRangeOffset = array ();
		for($i = 0; $i < $segCount; $i ++) {
			$idRangeOffset [] = $this->read_ushort ();
		}
		
		for($n = 0; $n < $segCount; $n ++) {
			$endpoint = ($endCount [$n] + 1);
			for($unichar = $startCount [$n]; $unichar < $endpoint; $unichar ++) {
				if ($idRangeOffset [$n] == 0)
					$glyph = ($unichar + $idDelta [$n]) & 0xFFFF;
				else {
					$offset = ($unichar - $startCount [$n]) * 2 + $idRangeOffset [$n];
					$offset = $idRangeOffset_start + 2 * $n + $offset;
					if ($offset >= $limit)
						$glyph = 0;
					else {
						$glyph = $this->get_ushort ( $offset );
						if ($glyph != 0)
							$glyph = ($glyph + $idDelta [$n]) & 0xFFFF;
					}
				}
				$charToGlyph [$unichar] = $glyph;
				if ($unichar < 196608) {
					$this->maxUniChar = max ( $unichar, $this->maxUniChar );
				}
				$glyphToChar [$glyph] [] = $unichar;
			}
		}
	}
	
	// Put the TTF file together
	function endTTFile(&$stm) {
		$stm = '';
		$numTables = count ( $this->otables );
		$searchRange = 1;
		$entrySelector = 0;
		while ( $searchRange * 2 <= $numTables ) {
			$searchRange = $searchRange * 2;
			$entrySelector = $entrySelector + 1;
		}
		$searchRange = $searchRange * 16;
		$rangeShift = $numTables * 16 - $searchRange;
		
		// Header
		if (_TTF_MAC_HEADER) {
			$stm .= (pack ( "Nnnnn", 0x74727565, $numTables, $searchRange, $entrySelector, $rangeShift )); // Mac
		} else {
			$stm .= (pack ( "Nnnnn", 0x00010000, $numTables, $searchRange, $entrySelector, $rangeShift )); // Windows
		}
		
		// Table directory
		$tables = $this->otables;
		ksort ( $tables );
		$offset = 12 + $numTables * 16;
		foreach ( $tables as $tag => $data ) {
			if ($tag == 'head') {
				$head_start = $offset;
			}
			$stm .= $tag;
			$checksum = $this->calcChecksum ( $data );
			$stm .= pack ( "nn", $checksum [0], $checksum [1] );
			$stm .= pack ( "NN", $offset, strlen ( $data ) );
			$paddedLength = (strlen ( $data ) + 3) & ~ 3;
			$offset = $offset + $paddedLength;
		}
		
		// Table data
		foreach ( $tables as $tag => $data ) {
			$data .= "\0\0\0";
			$stm .= substr ( $data, 0, (strlen ( $data ) & ~ 3) );
		}
		
		$checksum = $this->calcChecksum ( $stm );
		$checksum = $this->sub32 ( array (
				0xB1B0,
				0xAFBA 
		), $checksum );
		$chk = pack ( "nn", $checksum [0], $checksum [1] );
		$stm = $this->splice ( $stm, ($head_start + 8), $chk );
		return $stm;
	}
	function repackageTTF($file, $TTCfontID = 0, $debug = false) {
		$this->filename = $file;
		$this->fh = fopen ( $file, 'rb' ) or die ( 'Can\'t open file ' . $file );
		$this->_pos = 0;
		$this->charWidths = '';
		$this->glyphPos = array ();
		$this->charToGlyph = array ();
		$this->tables = array ();
		$this->otables = array ();
		$this->ascent = 0;
		$this->descent = 0;
		$this->numTTCFonts = 0;
		$this->TTCFonts = array ();
		$this->skip ( 4 );
		$this->maxUni = 0;
		if ($TTCfontID > 0) {
			$this->version = $version = $this->read_ulong (); // TTC Header version
			                                                  // now
			if (! in_array ( $version, array (
					0x00010000,
					0x00020000 
			) ))
				die ( "ERROR - Error parsing TrueType Collection: version=" . $version . " - " . $file );
			$this->numTTCFonts = $this->read_ulong ();
			for($i = 1; $i <= $this->numTTCFonts; $i ++) {
				$this->TTCFonts [$i] ['offset'] = $this->read_ulong ();
			}
			$this->seek ( $this->TTCFonts [$TTCfontID] ['offset'] );
			$this->version = $version = $this->read_ulong (); // TTFont version
				                                                  // again now
		}
		$this->readTableDirectory ( $debug );
		$tags = array (
				'OS/2',
				'cmap',
				'glyf',
				'head',
				'hhea',
				'hmtx',
				'loca',
				'maxp',
				'name',
				'post',
				'cvt ',
				'fpgm',
				'gasp',
				'prep' 
		);
		/*
		 * Tables which require glyphIndex hdmx kern LTSH Tables which do NOT require glyphIndex VDMX GDEF GPOS GSUB JSTF DSIG PCLT - not recommended
		 */
		
		foreach ( $tags as $tag ) {
			if (isset ( $this->tables [$tag] )) {
				$this->add ( $tag, $this->get_table ( $tag ) );
			}
		}
		fclose ( $this->fh );
		$stm = '';
		$this->endTTFile ( $stm );
		return $stm;
	}
}

?>